谷粒商城笔记+踩坑——上架商品spu到ES索引库
Posted vincewm
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了谷粒商城笔记+踩坑——上架商品spu到ES索引库相关的知识,希望对你有一定的参考价值。
目录
2.6.3、【公共模块】商品常量类,添加上传成功或失败的状态码
1、ES回顾
elasticsearch基础1——索引、文档_elasticsearch索引 文档_vincewm的博客-CSDN博客
elasticsearch基础2——DSL查询文档,黑马旅游项目查询功能_elasticsearch查询文档_vincewm的博客-CSDN博客elasticsearch基础3——聚合、补全、集群_vincewm的博客-CSDN博客elasticsearch基础2——DSL查询文档,黑马旅游项目查询功能_elasticsearch查询文档_vincewm的博客-CSDN博客
2、ES整合商品上架
2.1、分析
es在整个项目中的应用:
1.对商品的全文检索功能
2.对日志的全文检索功能
为什么不用mysql?
es比mysql检索功能强大,并且对于庞大的检索数据,es性能更好,因为mysql是存在内存中的,而es可以分片存储。
需求:
- 在后台选择上架的商品才能在网站展示
- 上架的商品也可以被ES检索
2.2、创建sku的es索引库
2.2.1、两种索引库设计方案分析
- 检索的时候输入名字,这样就是需要用sku的名称去全文检索
- 检索的时候选择商品规格,规格是spu的公共属性,每个spu是一样的
索引库设计方案1(推荐,空间换时间):规格参数放在sku里
缺点:如果每个sku都存储规格参数(如尺寸),会有冗余存储,因为每个spu下面的sku规格参数都一样。例如spu“华为14Pro”下的sku“红色华为14Pro”,以及spu“华为手机”下的sku“蓝色华为14Pro”,这两个sku的规格参数“CPU:A14”相等。
skuId:1
spuId:11
skyTitile:华为xx
price:999
saleCount:99
attr:[
尺寸:5,
CPU:高通945,
分辨率:全高清
]
索引库设计方案2(不推荐,传输的数据量大):规格参数和sku分离
sku索引
spuId:1
skuId:11
attr索引
skuId:11
attr:[
尺寸:5,
CPU:高通945,
分辨率:全高清
]
结论:如果将规格参数单独建立索引,会出现检索时出现大量数据传输的问题,会引起网络故障。
所以我们选方案一,用空间换时间
2.2.2、最终选用的索引库方案
“type”: “keyword” , 保持数据精度问题,可以检索,但不分词
“analyzer”: “ik_smart” 中文分词器
“index”: false, 不可被检索,不生成index
“doc_values”: false 默认为true,不可被聚合,es就不会维护一些聚合的信息
这个数据模型要先在es中建立
PUT product
"mappings":
"properties":
"skuId": "type": "long" , #商品sku
"spuId": "type": "keyword" , #当前sku所属的spu。
"skuTitle":
"type": "text",
"analyzer": "ik_smart" #只有sku的标题需要被分词
,
"skuPrice": "type": "keyword" ,
"skuImg" : "type": "keyword" ,
"saleCount": "type":"long" ,
"hasStock": "type": "boolean" , #是否有库存。在库存模块添加此商品库存后,此字段更为true
"hotScore": "type": "long" ,
"brandId": "type": "long" ,
"catalogId": "type": "long" ,
"brandName": "type": "keyword",
"brandImg":
"type": "keyword",
"index": false,
"doc_values": false
,
"catalogName": "type": "keyword" ,
"attrs":
"type": "nested",
"properties":
"attrId": "type": "long" ,
"attrName":
"type": "keyword",
"index": false,
"doc_values": false
,
"attrValue": "type": "keyword"
2.3、SkuEsModel模型类
商品上架需要在es中保存spu信息并更新spu状态信息,所以我们就建立专门的vo来接收
SkuEsModel
写在common模块
@Data
public class SkuEsModel //common中
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<Attr> attrs;
@Data
public static class Attr
private Long attrId;
private String attrName;
private String attrValue;
2.4、【库存模块】库存量查询
查询sku列表是否有库存:
上架的话需要确定库存,所以调用ware微服务来检测是否有库存
@RestController
@RequestMapping("ware/waresku")
public class WareSkuController
@Autowired
private WareSkuService wareSkuService;
@PostMapping(value = "/hasStock")
public R getSkuHasStock(@RequestBody List<Long> skuIds)
List<SkuHasStockVo> vos = wareSkuService.getSkuHasStock(skuIds);
return R.ok().setData(vos);
实现也比较好理解,就是先用自定义的mapper查有没有库存
有的话,给库存赋值,并收集成集合
@Override
public List<SkuHasStockVo> getSkuHasStock(List<Long> skuIds)
List<SkuHasStockVo> skuHasStockVos = skuIds.stream().map(item ->
//根据sku_id查库存,要写mapper,主要为了真实库存减去锁定库存
Long count = this.baseMapper.getSkuStock(item);
SkuHasStockVo skuHasStockVo = new SkuHasStockVo();
skuHasStockVo.setSkuId(item);
skuHasStockVo.setHasStock(count == null ? false : count > 0);
return skuHasStockVo;
).collect(Collectors.toList());
return skuHasStockVos;
自定义mapper
这里的库存并不是简单查一下库存表,需要自定义一个简单的sql。用库存减去锁定的库存即可得出!
<select id="getSkuStock" resultType="java.lang.Long">
SELECT SUM(stock - stock_locked) FROM wms_ware_sku WHERE sku_id = #skuId
</select>
2.5、【查询模块】保存ES文档
2.5.1、常量类
public class EsConstant
//在es中的索引
public static final String PRODUCT_INDEX = "gulimall_product";
public static final Integer PRODUCT_PAGESIZE = 16;
2.5.2、controller
ElasticSaveController
package com.xxx.gulimall.search.controller;
@Slf4j
@RequestMapping(value = "/search/save")
@RestController
public class ElasticSaveController
@Autowired
private ProductSaveService productSaveService;
/**
* 上架商品
* @param skuEsModels
* @return
*/
@PostMapping(value = "/product")
public R productStatusUp(@RequestBody List<SkuEsModel> skuEsModels)
boolean status=false;
try
status = productSaveService.productStatusUp(skuEsModels);
catch (IOException e)
//log.error("商品上架错误",e);
return R.error(BizCodeEnum.PRODUCT_UP_EXCEPTION.getCode(),BizCodeEnum.PRODUCT_UP_EXCEPTION.getMessage());
if(status)
return R.error(BizCodeEnum.PRODUCT_UP_EXCEPTION.getCode(),BizCodeEnum.PRODUCT_UP_EXCEPTION.getMessage());
else
return R.ok();
2.5.3、service
使用BulkRequest ,批量保存sku_es模型类列表到索引库
@Slf4j
@Service("productSaveService")
public class ProductSaveServiceImpl implements ProductSaveService
@Autowired
private RestHighLevelClient esRestClient;
@Override
public boolean productStatusUp(List<SkuEsModel> skuEsModels) throws IOException
//1.在es中建立索引,建立号映射关系(doc/json/product-mapping.json)[kibana中执行product-mapping.txt,需要ES安装IK分词器]
//2. 在ES中保存这些数据
BulkRequest bulkRequest = new BulkRequest();
for (SkuEsModel skuEsModel : skuEsModels)
//构造保存请求
IndexRequest indexRequest = new IndexRequest(EsConstant.PRODUCT_INDEX);
indexRequest.id(skuEsModel.getSkuId().toString());
String jsonString = JSON.toJSONString(skuEsModel);
indexRequest.source(jsonString, XContentType.JSON);
bulkRequest.add(indexRequest);
BulkResponse bulk = esRestClient.bulk(bulkRequest, GulimallElasticSearchConfig.COMMON_OPTIONS);
//TODO 如果批量错误
boolean hasFailures = bulk.hasFailures();
List<String> collect = Arrays.asList(bulk.getItems()).stream().map(item ->
return item.getId();
).collect(Collectors.toList());
log.info("商品上架完成:",collect);
return hasFailures;
2.6、【商品模块】上架单个spu
2.6.1、controller
SpuInfoController上架
/**
* 商品上架
*/
@PostMapping("/spuId/up")
public R spuUp(@PathVariable("spuId") Long spuId)
spuInfoService.up(spuId);
return R.ok();
2.6.2、远程调用库存模块
在商品模块的feign包下:
@FeignClient("gulimall-ware")
public interface WareFeignService
@PostMapping(value = "/ware/waresku/hasStock")
R getSkuHasStock(@RequestBody List<Long> skuIds);
商品模块启动类:
@EnableFeignClients(basePackages = "com.xunqi.gulimall.product.feign")
然后service里就能直接@Autowired注入了。
2.6.3、【公共模块】商品常量类,添加上传成功或失败的状态码
public class ProductConstant
public enum AttrEnum
ATTR_TYPE_BASE(1,"基本属性"),
ATTR_TYPE_SALE(0,"销售属性");
private int code;
private String msg;
public int getCode()
return code;
public String getMsg()
return msg;
AttrEnum(int code, String msg)
this.code = code;
this.msg = msg;
public enum ProductStatusEnum
NEW_SPU(0,"新建"),
SPU_UP(1,"商品上架"),
SPU_DOWN(2,"商品下架"),
;
private int code;
private String msg;
public int getCode()
return code;
public String getMsg()
return msg;
ProductStatusEnum(int code, String msg)
this.code = code;
this.msg = msg;
2.6.4、service
业务流程:
- 查询当前spu下的sku列表;
- 将这个sku列表封装成sku_es模型类列表;
- 给每个sku加上属性规格列表;
- 查询每个sku是否有库存,要远程调用库存模块;
- 给每个sku加上热度、所属品牌、所属分类名、所有可被检索的规格等属性;
- 将收集的sku_es模型类列表发给es保存,要远程调用查询模块。
SpuInfoServiceImpl
@Override
public void up(Long spuId)
//1.获得spu对应的sku集合
List<SkuInfoEntity> skuInfoEntities = skuInfoService.getSkusBySpuId(spuId);
//2.获得spu的基础属性实体集合
List<ProductAttrValueEntity> baseAttrs = productAttrValueService.baseAttrListforSpu(spuId);
//3.获得基本属性中可搜索的属性id
//3.1获得spu基础属性实体集合中的属性id集合
List<Long> attrIds = baseAttrs.stream().map(attr ->
return attr.getAttrId();
).collect(Collectors.toList());
//3.2获得可搜索属性实体类对象
List<Long> searchAttrIds = attrService.selectSearchAttrs(attrIds);
//3.3将它们转化为set集合
Set<Long> idSet = searchAttrIds.stream().collect(Collectors.toSet());
//3.4对所有基础属性实体过滤,第一步是只保留可搜索属性实体类对象,第二步是给这些对象中的Attrs对象赋值,最后收集为attrsList
List<SkuEsModel.Attrs> attrsList = baseAttrs.stream().filter(item ->
return idSet.contains(item.getAttrId());
).map(item ->
SkuEsModel.Attrs attrs = new SkuEsModel.Attrs();
BeanUtils.copyProperties(item, attrs);
return attrs;
).collect(Collectors.toList());
//收集所有skuId的集合
List<Long> skuIdList = skuInfoEntities.stream()
.map(SkuInfoEntity::getSkuId)
.collect(Collectors.toList());
//TODO 1、发送远程调用,库存系统查询是否有库存
Map<Long, Boolean> stockMap = null;
try
R skuHasStock = wareFeignService.getSkuHasStock(skuIdList);
TypeReference<List<SkuHasStockVo>> typeReference = new TypeReference<List<SkuHasStockVo>>() ;
stockMap = skuHasStock.getData(typeReference).stream()
.collect(Collectors.toMap(SkuHasStockVo::getSkuId, item -> item.getHasStock()));
catch (Exception e)
log.error("库存服务查询异常:原因",e);
//2、封装每个sku的信息
Map<Long, Boolean> finalStockMap = stockMap;
List<SkuEsModel> collect = skuInfoEntities.stream().map(sku ->
//组装需要的数据
SkuEsModel esModel = new SkuEsModel();
esModel.setSkuPrice(sku.getPrice());
esModel.setSkuImg(sku.getSkuDefaultImg());
//设置库存信息
if (finalStockMap == null)
esModel.setHasStock(true);
else
esModel.setHasStock(finalStockMap.get(sku.getSkuId()));
//TODO 2、热度评分。0
esModel.setHotScore(0L);
//TODO 3、查询品牌和分类的名字信息
BrandEntity brandEntity = brandService.getById(sku.getBrandId());
esModel.setBrandName(brandEntity.getName());
esModel.setBrandId(brandEntity.getBrandId());
esModel.setBrandImg(brandEntity.getLogo());
CategoryEntity categoryEntity = categoryService.getById(sku.getCatalogId());
esModel.setCatalogId(categoryEntity.getCatId());
esModel.setCatalogName(categoryEntity.getName());
//设置检索属性
esModel.setAttrs(attrsList);
BeanUtils.copyProperties(sku,esModel);
return esModel;
).collect(Collectors.toList());
//TODO 5、将数据发给es进行保存:mall-search
R r = searchFeignService.productStatusUp(collect);
if (r.getCode() == 0)
//远程调用成功
//TODO 6、修改当前spu的状态,具体代码看代码块后面SpuInfoDao.xml
this.baseMapper.updaSpuStatus(spuId, ProductConstant.ProductStatusEnum.SPU_UP.getCode());
else
//远程调用失败
//TODO 7、重复调用?接口幂等性:重试机制
SpuInfoDao更新spu状态
<update id="updaSpuStatus"> UPDATE pms_spu_info SET publish_status = #code ,update_time = NOW() WHERE id = #spuId </update>
2.6.5、测试
商品上架用到了三个微服务,分别是product、ware、search
那我们分别debug启动它们,然后在这些微服务中使用的方法中打上断点,查看调用流程
基础属性如下:
测试
2.6.6、修改结果类R
public <T> T getData(String key, TypeReference<T> typeReference)
Object data = get(key);// 默认是map类型,springmvc做的
String jsonStr = JSON.toJSONString(data);
T t = JSON.parseObject(jsonStr, typeReference);
return t;
// 利用fastJson进行逆转
// 这里要声明泛型<T>,这个泛型只跟方法有关,跟类无关。
// 例如类上有个泛型,这里可以使用类上的泛型,就不用声明
public <T> T getData(TypeReference<T> typeReference)
Object data = get("data");// 默认是map类型,springmvc做的
String jsonStr = JSON.toJSONString(data);
T t = JSON.parseObject(jsonStr, typeReference);
return t;
public R setData(Object data)
put("data", data);
return this;
Day409.商品上架 -谷粒商城
一、商品上架
1、项目架构图
2、product-es准备
ES在内存中,所以在检索中优于mysql。ES也支持集群
,数据分片存储。
-
需求:
- 上架的商品才可以在网站展示。
- 上架的商品需要可以被检索。(
上架的商品就加入到es去被检索
) - 分析sku在es中如何存储 商品mapping
-
分析: 商品上架在es中是存sku还是spu?
- 1)检索的时候
输入名字
,是需要按照sku的title进行全文检索
- 2)检素使用
商品规格
,规格是spu的公共属性
,每个spu是一样的 - 3)按照
分类id
进去的都是直接列出spu
的,还可以切换。 - 4〕我们如果将sku的全量信息保存到es中(包括spu属性〕就太多字段了
- 1)检索的时候
-
处理方案
-
方案1:
存储的到
一个索引
记录中,每个都带有对应sku具体的attr属性-
{ skuId:1 spuId:11 skyTitile:华为xx price:999 saleCount:99 attr:[ {尺寸:5}, {CPU:高通945}, {分辨率:全高清} ]
-
缺点:
- 如果每个sku都存储规格参数(如尺寸),会有
冗余存储
,因为每个spu对应的sku的规格参数都一样
- 如果每个sku都存储规格参数(如尺寸),会有
-
-
方案2:
存储到
两个索引
记录中,一个存储对应的spu/sku的id,然后去检索查询另一个记录中的数据,就可以解决上面的冗余存储的问题-
#sku索引 { spuId:1 skuId:11 } #attr索引 { skuId:11 attr:[ {尺寸:5}, {CPU:高通945}, {分辨率:全高清} ] }
-
缺点:
- 先找到4000个符合要求的spu,再根据4000个spu查询对应的属性,封装了4000个id,long 8B*4000=32000B=32KB 1K个人检索,就是32MB
- 所以会出现如果有大量并发的请求,就会出现一刹那32MB,我们就加两个0,就会有3g多的数据在网络传输
- 这样子就会出现
网络问题
-
-
结论
如果将规格参数单独建立索引,会出现检索时出现大量数据传输的问题,会引起网络网络
因此选用
方案1
,以空间换时间
-
-
建立product索引
索引数据模型
PUT product { "mappings":{ "properties": { "skuId":{ "type": "long" }, "spuId":{ "type": "keyword" }, # 不可分词 "skuTitle": { "type": "text", "analyzer": "ik_smart" # 中文分词器 }, "skuPrice": { "type": "keyword" }, "skuImg" : { "type": "keyword" }, "saleCount":{ "type":"long" }, "hasStock": { "type": "boolean" }, "hotScore": { "type": "long" }, "brandId": { "type": "long" }, "catalogId": { "type": "long" }, "brandName": {"type": "keyword"}, "brandImg":{ "type": "keyword", "index": false, # 不可被检索,不生成index "doc_values": false # 不可被聚合 }, "catalogName": {"type": "keyword" }, "attrs": { "type": "nested", "properties": { "attrId": {"type": "long" }, "attrName": { "type": "keyword", "index": false, "doc_values": false }, "attrValue": {"type": "keyword" } } } } } }
其中
- “type”: “keyword” 保持数据精度问题,可以检索,但不分词
- “index”:false 代表不可被检索
- “doc_values”: false 不可被聚合,es就不会维护一些聚合的信息
- 冗余存储的字段: 不用来检索,也不用来分析,节省空间
- 库存是bool。
- 检索品牌id,但是不检索品牌名字、图片 用skuTitle检索
-
nested嵌入式对象
属性是"type": “nested”,因为是内部的属性进行检索
数组类型的对象会被扁平化处理
(对象的每个属性会分别存储到一起)
user.name=["aaa","bbb"]
user.addr=["ccc","ddd"]
这种存储方式,可能会发生如下错误:
错误检索到{aaa,ddd},这个组合是不存在的
数组的扁平化处理会使检索能检索到本身不存在的,为了解决这个问题,就采用了嵌入式属性
,数组里是对象时用嵌入式属性(不是对象无需用嵌入式属性)
nested阅读:https://blog.csdn.net/weixin_40341116/article/details/80778599
使用聚合:https://blog.csdn.net/kabike/article/details/101460578
3、正式编码
- controller
//POST /product/spuinfo/{spuId}/up
//上架
@PostMapping("/{spuId}/up")
public R spuUp(@PathVariable("spuId")Long spuId){
spuInfoService.up(spuId);
return R.ok();
}
- 传输to对象
因为这里的search服务也需要使用到这个to对象,所以我们新建在common中
商品上架需要在es中保存spu信息并更新spu的状态信息,由于SpuInfoEntity与索引的数据模型并不对应,所以我们要建立专门的vo进行数据传输
com.achang.common.to.es.SkuEsModel
@Data
public class SkuEsModel implements Serializable {
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;
}
}
- service
com.achang.achangmall.product.service.impl.SpuInfoServiceImpl
//商品上架
@Override
public void up(Long spuId) {
//1、查询当前spuId对应的所有sku信息,品牌的名字
List<SkuInfoEntity> skuInfoEntities = skuInfoService.getSkusBySpuId(spuId);
List<Long> skuIdList = skuInfoEntities.stream().map(item -> {
return item.getSkuId();
}).collect(Collectors.toList());
//todo 查询当前sku可以被检索的规格属性
List<ProductAttrValueEntity> baseAttrList = productAttrValueService.baseAttrList(spuId);
List<Long> attrIdList = baseAttrList.stream().map(item -> {
return item.getAttrId();
}).collect(Collectors.toList());
List<Long> searchAttrIds = attrService.selectSearchAttrIds(attrIdList);
Set<Long> idSet = new HashSet<>(searchAttrIds);
List<SkuEsModel.Attrs> attrsList = baseAttrList.stream().filter(item -> {
return idSet.contains(item.getAttrId());
}).map(s -> {
SkuEsModel.Attrs attrsEs = new SkuEsModel.Attrs();
BeanUtils.copyProperties(s, attrsEs);
return attrsEs;
}).collect(Collectors.toList());
Map<Long, Boolean> stockMap = null;
try {
//发送远程调用,库存系统查询是否有库存
R r = wareFeignService.getSkuHasStock(skuIdList);
TypeReference<List<SkuHasStockVo>> typeReference = new TypeReference<List<SkuHasStockVo>>() {};
stockMap = r.getData(typeReference).stream().collect(Collectors.toMap(SkuHasStockVo::getSkuId, SkuHasStockVo::getHasStock));
}catch (Exception e){
log.error("库存服务查询出现问题:原因【{}】",e);//如果远程调用失败
}
//2、封装每个sku的信息
Map<Long, Boolean> finalStockMap = stockMap;
List<SkuEsModel> collect = skuInfoEntities.stream().map(i -> {
SkuEsModel model = new SkuEsModel();
BeanUtils.copyProperties(i,model);
model.setSkuPrice(i.getPrice());
model.setSkuImg(i.getSkuDefaultImg());
model.setHasStock( (finalStockMap != null)? finalStockMap.get(i.getSkuId()):true );//设置库存信息
//热度评分 0
model.setHotScore(0L);
//查询品牌 和 分类名字信息
BrandEntity brandEntity = brandService.getById(model.getBrandId());
model.setBrandName(brandEntity.getName());
model.setBrandImg(brandEntity.getLogo());
CategoryEntity categoryEntity = categoryService.getById(model.getCatalogId());
model.setCatalogName(categoryEntity.getName());
//设置检索属性
model.setAttrs(attrsList);
return model;
}).collect(Collectors.toList());
//发给es进行保存,远程调用
R r = esFeignService.productStatusUp(collect);
String code = (String) r.get("code");
if (Integer.parseInt(code) == 0){
//调用成功
//todo 修改spu商品状态
this.baseMapper.updateSpuStatus(spuId, ProductConsatnt.StatusEnum.SPU_UP.getCode());
}else {
//todo 调用失败,重复调用,接口幂等性;重试机制
}
}
- 在指定的所有属性集合中,筛选中检索属性
com.achang.achangmall.product.service.impl.AttrServiceImpl
//在指定的所有属性集合中,筛选中检索属性
@Override
public List<Long> selectSearchAttrIds(List<Long> attrIdList) {
List<AttrEntity> attrEntities = this.baseMapper.selectList(new QueryWrapper<AttrEntity>()
.eq("search_type", 1)
.in("attr_id", attrIdList).select("attr_id"));
List<Long> searchAttrIdList = attrEntities.stream().map(item -> {
return item.getAttrId();
}).collect(Collectors.toList());
return searchAttrIdList;
}
- 远程调用接口—库存服务
com.achang.achangmall.ware.controller.WareSkuController
//查询sku是否有库存
@PostMapping("/hasstock")
public R getSkuHasStock(@RequestBody List<Long> skuIdList){
List<SkuHasStockVo> vos = wareSkuService.getSkuHasStock(skuIdList);
return R.ok().put("data",vos);
}
com.achang.achangmall.ware.vo.SkuHasStockVo
@Data
public class SkuHasStockVo {
private Long skuId;
private Boolean hasStock;
}
com.achang.achangmall.ware.controller.WareSkuController
//查询sku是否有库存
@PostMapping("/hasstock")
public R getSkuHasStock(@RequestBody List<Long> skuIdList){
List<SkuHasStockVo> vos = wareSkuService.getSkuHasStock(skuIdList);
return R.ok().setData(vos);
}
com.achang.achangmall.ware.service.impl.WareSkuServiceImpl
@Override
public List<SkuHasStockVo> getSkuHasStock(List<Long> skuIdList) {
List<SkuHasStockVo> collect = skuIdList.stream().map(item -> {
SkuHasStockVo vo = new SkuHasStockVo();
Long count = baseMapper.getSkuStock(item);
vo.setSkuId(item);
vo.setHasStock(count == null?false:count>0);
return vo;
}).collect(Collectors.toList());
return collect;
}
achangmall-ware/src/main/resources/mapper/ware/WareSkuDao.xml
<select id="getSkuStock" resultType="java.lang.Long">
SELECT SUM(stock - stock_locked)
FROM `wms_ware_sku`
WHERE `sku_id` = #{item}
</select>
远程调用com.achang.achangmall.product.feign.WareFeignService
@FeignClient("achangmall-ware")
public interface WareFeignService {
@PostMapping("/ware/waresku/hasstock")
public R getSkuHasStock(@RequestBody List<Long> skuIdList);
}
远程调用ES添加索引功能
com.achang.achangmall.search.controller.ESSaveController
@RequestMapping("/search")
@RestController
@Slf4j
public class ESSaveController {
@Autowired
private ProductSaveService productSaveService;
//上架商品
@PostMapping("/save/product")
public R productStatusUp(@RequestBody List<SkuEsModel> esModels) {
Boolean flag = false;
try {
flag = productSaveService.productStatusUp(esModels);
} catch (IOException e) {
e.printStackTrace();
log.error("ESSaveController,商品上架错误:{}",e);
return R.error(BizCodeEnum.PRODUCT_UP_EXCEPTION.getCode(),BizCodeEnum.PRODUCT_UP_EXCEPTION.getMsg());
}
if (!flag){
return R.ok();
}else {
return R.error(BizCodeEnum.PRODUCT_UP_EXCEPTION.getCode(),BizCodeEnum.PRODUCT_UP_EXCEPTION.getMsg());
}
}
}
com.achang.achangmall.search.constant.ESConstant
public class ESConstant {
public static final String PRODUCT_INDEX = "product";//sku数据在es中的索引
}
com.achang.achangmall.search.service.impl.ProductSaveServiceImpl
@Slf4j
@Service
public class ProductSaveService以上是关于谷粒商城笔记+踩坑——上架商品spu到ES索引库的主要内容,如果未能解决你的问题,请参考以下文章
商城项目20_sku在es中储存模型分析索引建立上架逻辑核心上架流程图