谷粒商城笔记+踩坑——上架商品spu到ES索引库

Posted vincewm

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了谷粒商城笔记+踩坑——上架商品spu到ES索引库相关的知识,希望对你有一定的参考价值。

目录

1、ES回顾

2、ES整合商品上架 

2.1、分析

2.2、创建sku的es索引库

2.2.1、两种索引库设计方案分析

2.2.2、最终选用的索引库方案

2.3、SkuEsModel模型类

2.4、【库存模块】库存量查询

2.5、【查询模块】保存ES文档

2.5.1、常量类

2.5.2、controller

2.5.3、service

2.6、【商品模块】上架单个spu

2.6.1、controller

2.6.2、远程调用库存模块

2.6.3、【公共模块】商品常量类,添加上传成功或失败的状态码

2.6.4、service

2.6.5、测试

2.6.6、修改结果类R


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

业务流程:

  1. 查询当前spu下的sku列表;
  2. 将这个sku列表封装成sku_es模型类列表;
    1. 给每个sku加上属性规格列表;
    2. 查询每个sku是否有库存,要远程调用库存模块;
    3. 给每个sku加上热度、所属品牌、所属分类名、所有可被检索的规格等属性;
  3. 将收集的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启动它们,然后在这些微服务中使用的方法中打上断点,查看调用流程

获得spu对应的sku集合

获得spu的基础属性实体集合

基础属性如下:

给SkuEsModel.Attrs对象赋值

测试

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

      存储的到一个索引记录中,每个都带有对应sku具体的attr属性

      • {
            skuId:1
            spuId:11
            skyTitile:华为xx
            price:999
            saleCount:99
            attr:[
                {尺寸:5},
                {CPU:高通945},
                {分辨率:全高清}
        	]
        
      • 缺点:

        • 如果每个sku都存储规格参数(如尺寸),会有冗余存储,因为每个spu对应的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索引库的主要内容,如果未能解决你的问题,请参考以下文章

谷粒商城--整合Elasticsearch和商品的上架

商城项目20_sku在es中储存模型分析索引建立上架逻辑核心上架流程图

商城项目20_sku在es中储存模型分析索引建立上架逻辑核心上架流程图

谷粒商城高级篇商品服务 & 商品上架

谷粒商城--SPU和SKU

Day407&408&409.ES -谷粒商城