谷粒商城-day13-es和商品上架

sku 在 es 当中的存储模型

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,  
                "doc_values": false
            },
            "catalogName": {"type": "keyword" },
            "attrs": {
                "type": "nested",
                "properties": {
                    "attrId": {"type": "long"  },
                    "attrName": {
                        "type": "keyword",
                        "index": false,
                        "doc_values": false
                    },
                    "attrValue": {"type": "keyword" }
                }
            }
        }
    }
}

nested 数据模型

es默认会对数组对象扁平化处理,导致结果出现意外情况,这是我们需要指定为 nested 嵌入式类型

DELETE my_index

PUT my_index/_doc/1
{
  "group" : "fans",
  "user" : [ 
    {
      "first" : "John",
      "last" :  "Smith"
    },
    {
      "first" : "Alice",
      "last" :  "White"
    }
  ]
}

GET my_index/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "user.first": "Alice"
          }
        },
        {
          "match": {
            "user.last": "Smith"
          }
        }
      ]
    }
  }
}

# 可以查得到数据,应该是无法查询到的

GET my_index/_mapping

# 删除后重新导入数据
DELETE my_index

PUT my_index
{
  "mappings": {
    "properties": {
      "user": {
        "type": "nested"
      }
    }
  }
}

PUT my_index/_doc/1
{
  "group" : "fans",
  "user" : [ 
    {
      "first" : "John",
      "last" :  "Smith"
    },
    {
      "first" : "Alice",
      "last" :  "White"
    }
  ]
}

GET my_index/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "user.first": "Alice"
          }
        },
        {
          "match": {
            "user.last": "Smith"
          }
        }
      ]
    }
  }
}
# 再次查询就查不到了

构造 sku 检索属性

这节课主要是构造 up 上架商品的接口的大体框架

controller

// SpuInfoController 
/// product/spuinfo/{spuinfo}/up
    @PostMapping("/{spuId}/up")
    public R spuUp(@PathVariable("spuId") Long spuId){
        spuInfoService.up(spuId);

        return R.ok();
    }

放在 common 的 to 中,这里基本是根据 es 构造的索引创建的 bean

@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;
    }

}

service

@Override
    public void up(Long spuId) {
        // 1. 查出当前 spuid 对应的所有 sku 信息,品牌的名字
        List<SkuInfoEntity> skus = skuInfoService.getSkuBySpuId(spuId);

        // TODO 4.查询当前 sku 的所有可以被检索的规格属性

        // 2. 封装每个 sku 的信息
        skus.stream().map(sku -> {
            // 组装需要的数据
            SkuEsModel esModel = new SkuEsModel();
            BeanUtils.copyProperties(sku, esModel);

            esModel.setSkuPrice(sku.getPrice());
            esModel.setSkuImg(sku.getSkuDefaultImg());
            // TODO 1.远程调用库存系统 是否有库存

            // TODO 2.热度评分.0.

            // TODO 3.品牌和分类的名字信息
            BrandEntity brand = brandService.getById(esModel.getBrandId());
            esModel.setBrandName(brand.getName());
            esModel.setBrandImg(brand.getLogo());

            CategoryEntity category = categoryService.getById(esModel.getCatalogId());
            esModel.setCatalogName(category.getName());

            return esModel;
        }).collect(Collectors.toList());

        // TODO 5. 将数据发送给 es 进行保存

    }

构造 sku 检索属性

其实不怎么复杂,基本的增删改查加上一些集合操作,体会下这里的 stream map 即可

@Override
    public void up(Long spuId) {
        // 1. 查出当前 spuid 对应的所有 sku 信息,品牌的名字
        List<SkuInfoEntity> skus = skuInfoService.getSkuBySpuId(spuId);

        // TODO 4.查询当前 sku 的所有可以被检索的规格属性
        List<ProductAttrValueEntity> baseAttrs = productAttrValueService.baseAttrListforspu(spuId);
        List<Long> attrIds = baseAttrs.stream().map(attr -> {
            return attr.getAttrId();
        }).collect(Collectors.toList());

        List<Long> searchAttrIds = attrService.selectSearchAttrs(attrIds);

        Set<Long> idSet = new HashSet<>(searchAttrIds);

        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());

        // 2. 封装每个 sku 的信息
        skus.stream().map(sku -> {
            // 组装需要的数据
            SkuEsModel esModel = new SkuEsModel();
            BeanUtils.copyProperties(sku, esModel);

            esModel.setSkuPrice(sku.getPrice());
            esModel.setSkuImg(sku.getSkuDefaultImg());
            // TODO 1.远程调用库存系统 是否有库存

            // TODO 2.热度评分.0
            esModel.setHotScore(0L);

            // TODO 3.品牌和分类的名字信息
            BrandEntity brand = brandService.getById(esModel.getBrandId());
            esModel.setBrandName(brand.getName());
            esModel.setBrandImg(brand.getLogo());

            CategoryEntity category = categoryService.getById(esModel.getCatalogId());
            esModel.setCatalogName(category.getName());

            // 设置检索属性
            esModel.setAttrs(attrsList);

            return esModel;
        }).collect(Collectors.toList());

        // TODO 5. 将数据发送给 es 进行保存

    }

远程查询库存&泛型结果对接

本节也比较简单,也是老套路,注意点有两个

  1. 远程调用接口使用泛型R,避免类型转换的麻烦
  2. 第二条之前提到过,远程调用要考虑到超时等情况 ,try catch 异常,set默认值
@Override
    public void up(Long spuId) {
        // 1. 查出当前 spuid 对应的所有 sku 信息,品牌的名字
        List<SkuInfoEntity> skus = skuInfoService.getSkuBySpuId(spuId);

        List<Long> skuIdList = skus.stream().map(SkuInfoEntity::getSkuId).collect(Collectors.toList());

        // TODO 1.远程调用库存系统 是否有库存
        Map<Long, Boolean> stockMap = null;
        try {
            R<List<SkuHasStockVo>> skuHasStock = wareFeignService.getSkusHasStock(skuIdList);
            stockMap = skuHasStock.getData().stream()
                    .collect(Collectors.toMap(SkuHasStockVo::getSkuId, item -> item.getHasStock()));
        } catch (Exception e) {
            log.error("库存服务异常:原因{}", e);
        }


        // TODO 4.查询当前 sku 的所有可以被检索的规格属性
        List<ProductAttrValueEntity> baseAttrs = productAttrValueService.baseAttrListforspu(spuId);
        List<Long> attrIds = baseAttrs.stream().map(attr -> {
            return attr.getAttrId();
        }).collect(Collectors.toList());

        List<Long> searchAttrIds = attrService.selectSearchAttrs(attrIds);

        Set<Long> idSet = new HashSet<>(searchAttrIds);

        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());

        // 2. 封装每个 sku 的信息
        Map<Long, Boolean> finalStockMap = stockMap;
        skus.stream().map(sku -> {
            // 组装需要的数据
            SkuEsModel esModel = new SkuEsModel();
            BeanUtils.copyProperties(sku, esModel);

            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 brand = brandService.getById(esModel.getBrandId());
            esModel.setBrandName(brand.getName());
            esModel.setBrandImg(brand.getLogo());

            CategoryEntity category = categoryService.getById(esModel.getCatalogId());
            esModel.setCatalogName(category.getName());

            // 设置检索属性
            esModel.setAttrs(attrsList);

            return esModel;
        }).collect(Collectors.toList());

        // TODO 5. 将数据发送给 es 进行保存

    }

给 R 添加泛型

/**
 * 返回数据
 *
 * @author Mark [email protected]
 */
public class R<T> extends HashMap<String, Object> {
    private static final long serialVersionUID = 1L;

    private T data;

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }

feign

@FeignClient("gulimall-ware")
public interface WareFeignService {

    /**
     * 1.R 设计的时候加上泛型
     * 

* 2.直接返回我们需要的数据 *

* 3.自己封装解析结果 * * @param skuIds * @return */ @PostMapping("/ware/waresku/hasstock") R<List<SkuHasStockVo>> getSkusHasStock(@RequestBody List<Long> skuIds); }

WareSkuController

// 查询 sku 是否有库存
    @PostMapping("/hasstock")
    public R<List<SkuHasStockVo>> getSkusHasStock(@RequestBody List<Long> skuIds) {
        // sku_id, stock
        List<SkuHasStockVo> vos = wareSkuService.getSkusHasStock(skuIds);

        R<List<SkuHasStockVo>> ok = R.ok();
        ok.setData(vos);

        return ok;
    }
@Override
    public List<SkuHasStockVo> getSkusHasStock(List<Long> skuIds) {
        List<SkuHasStockVo> collect = skuIds.stream().map(skuId -> {
            SkuHasStockVo vo = new SkuHasStockVo();
            // 查看当前 sku 的库存总量
            long count = baseMapper.getSkuStock(skuId);

            vo.setSkuId(skuId);
            vo.setHasStock(count>0);
            return vo;
        }).collect(Collectors.toList());

        return collect;
    }
// mapper
    long getSkuStock(Long skuId);
<select id="getSkuStock" resultType="java.lang.Long">
        select SUM(stock - stock_locked)
        from `wms_ware_sku`
        where sku_id = #{skuId};
    select>

远程上架

这里的实现思路是调用远程服务 search ,在 search 模块里面把 商品的数据传到 es ,远程调用成功把spu状态改为已上架

但这里上架成功之后,没有修改上架状态

抽取响应结果

谷粒商城-day13-es和商品上架_第1张图片

谷粒商城-day13-es和商品上架_第2张图片

这里要提一嘴,这里雷神又debug了下,因为 R 是 hashmap ,序列化时会失效,改了另外一种方式,用了泛型

public R setData(Object data) {
        put("data", data);
        return this;
    }

    // 利用 fastjson 进行你装
    public <T> T getData(TypeReference<T> typeReference) {
        Object data = get("data"); // 默认是 map
        String s = JSON.toJSONString(data);

        T t = JSON.parseObject(s, typeReference);
        return t;
    }
R r = wareFeignService.getSkusHasStock(skuIdList);
            
            TypeReference<List<SkuHasStockVo>> typeReference = new TypeReference<List<SkuHasStockVo>>(){};
            stockMap = r.getData(typeReference).stream()
                    .collect(Collectors.toMap(SkuHasStockVo::getSkuId, item -> item.getHasStock()));

其实这个功能我这边也常用,就是map转具体类的,不过用的形参不是TypeReference,直接传的 Class ,下面给出我这边的工具类供参考

public class MapObjectUtil {
    // 改为 借助于 json 对象实现,兼容现在的 json 转换体系
    //map转java对象
    public static Object mapToObjectJson(Map<String, Object> map, Class<?> beanClass) throws Exception {
        String jsonStr = JSONObject.toJSONString(map);
        return JSONObject.parseObject(jsonStr, beanClass);
    }

    //java对象转map
    public static Map<String, Object> objectToMapJson(Object obj) {
        String jsonStr = JSONObject.toJSONString(obj);
        return JSONObject.parseObject(jsonStr);
    }
    
}

结束

上架商品的功能就写到这里了,后续应该还会再完善简化下,其实很多都是重复的增删改查,主要还是看看 es 的应用场景,还有 远程调用的时候处理的一些细节,但可能这块得到的知识容量还是不怎么大

你可能感兴趣的:(谷粒商城,elasticsearch,java,lucene)