• 谷粒商城-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" }
                    }
                }
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    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"
              }
            }
          ]
        }
      }
    }
    # 再次查询就查不到了
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90

    构造 sku 检索属性

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

    controller

    // SpuInfoController 
    /// product/spuinfo/{spuinfo}/up
        @PostMapping("/{spuId}/up")
        public R spuUp(@PathVariable("spuId") Long spuId){
            spuInfoService.up(spuId);
    
            return R.ok();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    放在 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;
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    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 进行保存
    
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    构造 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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53

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

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

    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 进行保存
    
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73

    给 R 添加泛型

    /**
     * 返回数据
     *
     * @author Mark sunlightcs@gmail.com
     */
    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;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

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

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    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;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    @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);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    <select id="getSkuStock" resultType="java.lang.Long">
            select SUM(stock - stock_locked)
            from `wms_ware_sku`
            where sku_id = #{skuId};
        select>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    远程上架

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

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

    抽取响应结果

    image-20220727114010916

    image-20220727152302369

    这里要提一嘴,这里雷神又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;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    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()));
    
    • 1
    • 2
    • 3
    • 4
    • 5

    其实这个功能我这边也常用,就是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);
        }
        
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    结束

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

  • 相关阅读:
    Echarts柱状图label优化历程
    创新医疗器械行业机会渐行渐近
    最大二叉树
    虚拟化、容器与Docker基本介绍以及安装部署(Docker 基本管理)
    asp.net core webapi signalR通信
    向量检索之一:Faiss 在工业界的应用和常见问题解决
    零基础入门MATLAB(一篇十分钟)
    大数据在电力行业的应用案例100讲(十二)-全链路设计
    2021 RoboCom 世界机器人开发者大赛-高职组(复赛)
    事件总线--EvenBus
  • 原文地址:https://blog.csdn.net/qq_39007838/article/details/126015607