商品上架业务

news2024/11/13 14:59:34

一.商品上架操作

将检索数据存入es,更改商品上架状态为已上架

二.业务设计

(1)设计检索数据

分析:商品上架在 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 的数据,,传输阻塞时间会很
长,业务更加无法继续。
所以,我们如下设计,这样才是文档区别于关系型数据库的地方,宽表设计,不能去考虑数
据库范式。

什么是sku和spu?
spu 例:华为 HUAWEI Mate 30 Pro
sku 例:华为 HUAWEI Mate 30 Pro 亮黑色 8GB+256GB麒麟990旗舰芯片OLED环幕屏双4000万徕卡电影四摄4G全网通手机
sku可以根据颜色、内存、芯片等各种规格参数来划分成不同的版本,sku为这款产品的最小单元,查询产品时主要查sku信息

从查询思路出发,查询商品信息是根据spu查sku,核心是sku,又因为spu与sku是一对多的关系,所有我们存储检索数据有两种方式
(1)spu与sku设计成一个索引,通过查询spu就能查询到每一条具体的sku数据,缺点是存入es数据量庞大,占用空间大(优先考虑)
(2)spu与sku分开,单独索引,查询时先查spu,然后再查出具体sku,虽然存入es数据量不大,但是查询效率低
检索字段主要包括:[spu的id,sku的id,sku的商品标题,sku部分商品参数,产品热度分数,是否有库存,产品部分属性参数],最后根据上面两种方式设计出数据的结构,这里选用第一种方式(空间换时间)宽表设计,不能去考虑数据库范式,将spu和sku信息整合在一起,下面是QUERY DSL新增语句:

PUT product
{
  "mappings": {
    "properties": {
      "skuId": {
        "type": "long"
      },
      "spuId": {
        "type": "long"
      },
      "skuTitle": {
        "type": "text",
        "analyzer": "ik_smart"
      },
      "skuPrice": {
        "type": "keyword"
      },
      "skuImg": {
        "type": "keyword",
        "index": false,
        "doc_values": false
      },
      "saleCount": {
        "type": "long"
      },
      "hosStock": {
        "type": "boolean"
      },
      "hotScore": {
        "type": "long"
      },
      "brandId": {
        "type": "long"
      },
      "catelogId": {
        "type": "long"
      },
      "brandName": {
        "type": "keyword",
        "index": false,
        "doc_values": false
      },
      "brandImg": {
        "type": "keyword",
        "index": false,
        "doc_values": false
      },
      "catelogName": {
        "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"
          }
        }
      }
    }
  }
}

(2)设计后端业务

  1. 查询当前sku规格属性,筛选出可以检索的属性
  2. 远程调用仓储服务查询是否有库存
  3. 远程调用es服务保存数据
  4. 修改商品上架状态

三.代码

(1)商品服务查数据

//商品上架:上架是将后台的商品放在 es 中可以提供检索和查询功能
    @Override
    public void up(Long spuId) {
        //根据spuid查出sku集合
        List<SkuInfoEntity> skuInfoEntities = skuInfoService.querySkuBySpuId(spuId);

        //TODO 4.查询当前sku规格属性,并判断是否可以检索 search_type
        /**
         * 根据Spuid查表`pms_product_attr_value`,取出attr_id,查这些属性idattr_id的属性
         * 筛选出符合检索的search_type=1的attr
         * 然后拿到 attr的attr_id=product_attr_value的attr_id 的数据插入SkuEsModel.Attrs
         */
        List<ProductAttrValueEntity> productAttrValueList = productAttrValueService.baseAttrlistforspu(spuId);
        //获取attrIds
        List<Long> attrIds = productAttrValueList.stream().map(productAttrValue -> {
            Long attrId = productAttrValue.getAttrId();
            return attrId;
        }).collect(Collectors.toList());

        //筛选出search_type=1的attr
        List<AttrEntity> list = attrDao.selectBatchIds(attrIds);
        List<Long> productAttrIds = list.stream().filter(attr -> {
            return attr.getSearchType() == 1;
        }).map(attrEntity -> {
            return attrEntity.getAttrId();
        }).collect(Collectors.toList());

        HashSet<Long> idSet = new HashSet<>(productAttrIds);

        //拿到search_type=1的product_attr_value数据
        List<SkuEsModel.Attrs> esAttrList = productAttrValueList.stream().filter(item -> {
            //筛选
            return idSet.contains(item.getAttrId());
        }).map(attr -> {
            //给SkuEsModel.Attrs赋值
            SkuEsModel.Attrs esAttr = new SkuEsModel.Attrs();
            BeanUtils.copyProperties(attr, esAttr);
            return esAttr;
        }).collect(Collectors.toList());

        //TODO 1.发送远程调用 库存系统查询是否有库存
        //注意调用时,传的参数有序列化问题
        List<Long> skuIds = skuInfoEntities.stream().map(SkuInfoEntity::getSkuId).collect(Collectors.toList());
        Map<Long, Boolean> booleanMap = null;
        try {
            R<List<SkuHasStockVo>> r = wareFeignService.getSkuHasStock(skuIds);
            Integer code = r.getCode();
            List<SkuHasStockVo> data = r.getData(new TypeReference<List<SkuHasStockVo>>(){});
            booleanMap = data.stream().collect(Collectors.toMap(SkuHasStockVo::getSkuId, item -> item.getHasStock()));
        } catch (Exception e) {
            log.error("库存服务查询异常:原因{}",e);
        }

        //循环查数据库?
        Map<Long, Boolean> finalBooleanMap = booleanMap;
        List<SkuEsModel> collect = skuInfoEntities.stream().map(sku -> {
            SkuEsModel esModel = new SkuEsModel();
            BeanUtils.copyProperties(sku, esModel);
            esModel.setSkuImg(sku.getSkuDefaultImg());
            esModel.setSkuPrice(sku.getPrice());
            esModel.setAttrs(esAttrList);

            //拿到远程查出的库存状态
            if (finalBooleanMap == null){
                //为null表示调用库存服务失败,直接给一个默认库存
                //有库存true  无库存false,默认有数据为true
                esModel.setHasStock(true);
            }else {
                //调用库存服务成功,直接设置库存状态
                esModel.setHasStock(finalBooleanMap.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());

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

        //TODO 5.es服务保存数据
        R r = searchFeignService.productStatusUp(collect);
        if (r.getCode() == 0){
            //TODO 6.修改商品上架状态 上架状态[0 - 下架,1 - 上架]
            this.baseMapper.updataSpuStatus(spuId, ProductConstant.StatusEnum.SUP_UP.getCode());
        }else {
            //远程调用失败
            //TODO 7.重复调用?接口幂等性:重试机制?
        }


    }

(2)仓储服务查库存

//查询是否有库存,参数传json
    @Override
    public List<SkuHasStockVo> getSkuHasStock(List<Long> skuIds) {
        /**
         * 库存 = 库存总合 - 锁定库存总和
         * 查每个skuid的库存
         *
         * 循环查SELECT SUM(stock-stock_locked) AS stocksum FROM `wms_ware_sku` WHERE sku_id=?
         * 分组查SELECT sku_id,SUM(stock-stock_locked) AS stocksum FROM `wms_ware_sku` GROUP BY sku_id
         */
        List<SkuHasStockVo> collect = skuIds.stream().map(skuId -> {
            Long count = baseMapper.selectSumStock(skuId);
            SkuHasStockVo skuHasStockVo = new SkuHasStockVo();
            skuHasStockVo.setSkuId(skuId);
            skuHasStockVo.setHasStock(count==null?false:count>0);
            return skuHasStockVo;
        }).collect(Collectors.toList());

        return collect;
    }
	<select id="selectSumStock" resultType="java.lang.Long">
        SELECT SUM(stock-stock_locked) AS stocksum FROM `wms_ware_sku` WHERE sku_id=#{skuId}
    </select>

(3)ES服务新增数据

//上架,保存es数据
    @Override
    public void productStatusUp(List<SkuEsModel> esModels) throws Exception {
        BulkRequest bulkRequest = new BulkRequest();
        for (SkuEsModel esModel : esModels) {
            IndexRequest indexRequest = new IndexRequest();
            indexRequest.index(EsConstant.PRODUCT_INDEX);
            indexRequest.id(esModel.getSkuId().toString());
            String string = JSON.toJSONString(esModel);
            indexRequest.source(string, XContentType.JSON);
            bulkRequest.add(indexRequest);
        }
        //批量保存
        BulkResponse bulk = client.bulk(bulkRequest, GulimallElasticSearchConfig.COMMON_OPTIONS);

        //如果批量插入错误,就抛异常
        if (bulk.hasFailures()){//有问题返回true
            throw new Exception("es批量保存异常");
        }
        //获取批量处理结果
        BulkItemResponse[] items = bulk.getItems();
        List<String> collect = Arrays.stream(items).map(item -> {
            return item.getId();
        }).collect(Collectors.toList());
        log.info("商品上架成功id:{},返回数据:{}",collect,bulk.toString());

    }

(4)解决公共返回类无法获取数据的问题
公共返回类set的参数无法获取,因为返回类继承HashMap,远程调用服务时序列化数据无法获取类的属性,只能从Map中拿数据
解决办法:直接将数据序列化操作(对象转json,json再转成需要的对象)

	public <T> T getData(TypeReference<T> typeReference) {
		Object data = get("data");
		String s = JSON.toJSONString(data);
		T t = JSON.parseObject(s, typeReference);
		return t;
	}

TypeReference内部类方式传参

List<SkuHasStockVo> data = r.getData(new TypeReference<List<SkuHasStockVo>>(){});

四.功能测试

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/594521.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

给大龄准备转行网络工程师的朋友一些建议

我一直认为&#xff0c;网络工程师是一个看能力而不是看年龄的工作。 大龄转行网络工程师到底有没有机会&#xff1f;很多三十多岁的朋友对于跨行业完全心里没底&#xff0c;冒然转行学习网工又不知道从何学起。今天就给大家整理几个在学习网络工程师的时候需要注意的地方&…

Linux系统下imx6ull QT编程—— C++数据封装与数据抽象(八)

Linux QT编程 文章目录 Linux QT编程前言一、数据封装二、数据抽象 前言 封装是面向对象编程中的把数据和操作数据的函数绑定在一起的一个概念&#xff0c;这样能避免受到外界的干扰和误用&#xff0c;从而确保了安全。数据封装引申出了另一个重要的 OOP 概念&#xff0c;即数…

用JavaScript做一个拼图游戏

喜欢的可以复制下面完整代码查看效果在自己本地查看效果 实现难度&#xff1a;不算大&#xff0c;毕竟是小游戏 开发工具&#xff1a;html&#xff0c;css&#xff0c;js&#xff0c;jquery 效果截图 完整代码 <!DOCTYPE html> <html lang"en"> <he…

24届秋招专场·双指针巧解链表套路题

你好&#xff0c;我是安然无虞。 文章目录 合并两个有序链表分隔链表合并K个有序链表链表中倒数最后K个节点变形题: 删除链表的倒数第N个节点链表的中点判断链表是否有环环形链表II相交链表 大家好, 好久不见了, 从今天开始, 后面会经常更新笔试面试真题, 准备今年24届秋招的小…

Centos7安装Kubernetes 1.27.2

目录 一、准备工作 二、容器运行时 三、安装kubelet 、kubeadm、 kubectl 四、配置CNI 五、安装nginx 一、准备工作 1、更新yum源安装 vim、net-tools等工具&#xff08;每个节点都执行&#xff09; yum update -yyum install vim -yyum install net-tools -y 2、配置每…

简单移位器结构介绍

移位器 一位可控移位器 其实是一个复杂的多路开关电路&#xff0c;根据不同控制信号&#xff0c;将输入左移或右移或不变。多位的移位可以简单串联这样的单元实现&#xff0c;但移位位数多时&#xff0c;该方法过于复杂&#xff0c;不实用并且速度很慢。 桶形移位器 由晶体管…

C.12 军事领域关系抽取:UIE Slim最新升级版含数据标注、serving部署、模型蒸馏、可视化高亮展示等,助力工业应用场景快速落地

NLP专栏简介:数据增强、智能标注、意图识别算法|多分类算法、文本信息抽取、多模态信息抽取、可解释性分析、性能调优、模型压缩算法等 专栏详细介绍:NLP专栏简介:数据增强、智能标注、意图识别算法|多分类算法、文本信息抽取、多模态信息抽取、可解释性分析、性能调优、模型…

Linux内核漏洞提权

目录 Linux提权辅助工具 内核漏洞本地用户提权 - linux-exploit-suggester测试 内核漏洞Web用户提权 - 利用脏牛dcow 内核漏洞本地用户提权 - 利用DirtyPipe&#xff08;脏管&#xff09; 配置安全SUID提权 Linux提权辅助工具 这些工具都是C\C编写的&#xff0c;需要在目…

vmware虚拟机设置双网卡

文章目录 1. 配置虚拟机NAT模式2. 配置虚拟机桥接网络2.1 通过USB网卡2.1.1 配置虚拟机桥接网卡ip:2.1.2 配置windows主机桥接网卡ip:2.1.3 配置板子ip: 2.2 通过路由器2.2.1 配置板子ip: NAT 网卡&#xff1a;Ubuntu 通过它上网&#xff0c;只要 Windows 能上网&#xff0c;Ub…

北邮22信通:实验六 由运放器构成的音频放大电路设计、仿真、测试报告

北邮22信通一枚~ 持续更新模电实验讲解 关注作者&#xff0c;解锁更多邮苑模电实验报告~ 获取更多文章&#xff0c;请访问专栏&#xff1a; 北邮22信通——电子电路_青山如墨雨如画的博客-CSDN博客 目录 实验目的&#xff1a; 设计要求&#xff1a; *补充&#xff1…

AI最新资讯

AI最新资讯 1.画图2.修图3.3D建模4.openai调用5.自媒体工具6.自动化网页制作 自从gpt火了之后&#xff0c;AIGC就更新很快&#xff0c;许多好用的插件都太多了&#xff0c;所以记录一下&#xff0c;方便之后用到。 1.画图 1.midjourney和playgroundAI我之前博客有写过。 2.最近…

Web基本漏洞--文件上传漏洞

目录 一、文件上传漏洞介绍 1.文件上传漏洞原理 2.文件上传漏洞识别 3. 攻击方式 4.文件上传漏洞的危害 5.文件上传漏洞的防御措施 6.文件上传漏洞的绕过 一、文件上传漏洞介绍 1.文件上传漏洞原理 文件上传漏洞是指由于程序员在对用户文件上传部分的控制不足或者处理缺…

ActiveMQ消息中间件简介

一、ActiveMQ简介 ActiveMQ是Apache出品&#xff0c;最流行的&#xff0c;能力强劲的开源消息总线。ActiveMQ是一个完全支持JMS1.1和J2EE1.4规范的JMS Provide实现。尽管JMS规范出台已经是很久的事情了&#xff0c;但是JMS在当今的J2EE应用中仍然扮演这特殊的地位。 二、Active…

3Dmax云渲染如何使用?一文带你了解云渲染

3ds Max是Autodesk公司推出的一款专业计算机图形和三维动画软件&#xff0c;被广泛应用于建筑、室内设计、电影、游戏、广告、工业设计等领域。它提供丰富的模型建模、纹理编辑、灯光设置、渲染等功能&#xff0c;可以制作出高质量的三维模型、动画和静态渲染。它具有强大的扩展…

【1++的Linux】之Linux权限

&#x1f44d;作者主页&#xff1a;进击的1 &#x1f929; 专栏链接&#xff1a;【1的Linux】 文章目录 一&#xff0c;什么是Linux权限&#xff1f;二&#xff0c;Linux权限管理2.1 文件访问者的分类2.2 文件类型和访问权限2.3 修改文件权限2.4 掩码的作用及其设置 三&#xf…

前沿技术|人工智能的崛起和发展历程

前言&#xff1a; 人工智能的作用是使计算机能够模仿人类智能和学习能力&#xff0c;从而实现自动化、智能化和优化决策的目标。 文章目录 人工智能背景介绍发展状态未来展望 总结 人工智能 背景 人工智能&#xff08;Artificial Intelligence&#xff0c;AI&#xff09;的产生…

29Maven高级

一、分模块设计与开发 1、介绍 2、tlias-pojo模块 然后把pojo复制过去。pom中加入lombok依赖 引入tlias-pojo依赖 3、tlias-utils模块 tlias-utils的pom.xml <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.…

高频面试八股文原理篇(一)hashmap原理相关

目录 引言 面试题&#xff1a;hashmap原理 原理 JDK7时HashMap的数据结构 jdk8中hashMap数据结构 hashMap怎么设置初始值的大小 jdk7和jdk8中HashMap的区别 为什么放在hashMap集合key部分的元素需要重写equals方法&#xff1f; concurrenthashmap为什么线程安全 高频面…

Linux文件的扩展属性

文件属性 Linux文件属性分为常规属性与扩展属性&#xff0c;其中扩展属性有两种&#xff1a;attr与xattr. 一般常规的文件属性由stat API 读取&#xff0c;一般是三种权限&#xff0c;ower, group&#xff0c;时间等。 扩展属性attr 用户态API ioctl(fd, FS_IOC32_SETFLAGS …