谷粒商城-商品上架

1.sku在es中的存储模型分析(spring整和es)

es中所有数据存在内存中,内存产品贵,能节省就节省,只保存有用的信息

两种保存方法:(空间换时间,时间换空间):

我们选空间换时间

ES中放这些东西:

"mappings": {

"properties": {

"skuId": {

"type": "long"

},

"spuId": {

"type": "keyword"

},

"skuTitle": {

"type": "text",

"analyzer": "ik_smart"

},

"skuPrice": {

"type": "keyword"

},

"skuImg": {

"type": "keyword",

"index": false,

"doc_values": false

},

"saleCount": {

"type": "long"

},

"hasStock": {

"type": "boolean"

},

"hotScore": {

"type": "long"

},

"brandId": {

"type": "long"

},

"catalogId": {

"type": "long"

},

"brandName": {

"type": "keyword",

"index": false,

"doc_values": false

},

"brandImg": {

"type": "keyword",

"index": false,

"doc_values": false

},

"catalogName": {

"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"

}

}

}

}

}

}

//nested嵌入式的数据类型,

因为数组类型不标记nested的数据处理时会被扁平化处理(如下图),所以检索时就会出现问题,当我们查询first包含Alice且last包含Smith时是会查到结果的,因为被扁平化处理,first数组里查到了Alice,而且last数组里也查到了smith,所以当数组中的每个单元是对象的时候我们就可以用nested的数据类型,这样就不会出现扁平化处理这个错误

2.构造基本数据(商品上架功能编写)

根据接口文档,来到product的spuinfo下:

因为es里存的是我们挑出来的有用的信息而且需要product服务和search服务通信,正常开发权限问题有可能需要在product和search里都建立一个esModel的TO类,但是我们为了方便开发,可以直接在common里建这个TO:

然后来到实现类里来写方法体:(业务逻辑,我们不仅要查是否有库存,也要把可检索的规格属性一起存进es里,方便检索)

1.查出当前spuid对应的所有sku信息,再通过sku信息拿到所有的skuId组成数组,我们用这个数组去查这些sku是否都有库存(发送远程调用gulimall-ware服务),查出来的结果,用了一个map来装(Long,Boolean)这里用到了fastjson进行逆转(把R里的数据转成我们想要的对象),然后把是否有库存和skuid设置进 esModel 这个TO类里.

2.通过brandService和esModel的brandId查出品牌相关的name和img,通过catalogService和esModel的catalogId查出三级分类相关的name,放进 esModel

3.通过spuid查出所有sku的规格属性,把规格属性的id组成一个数组,遍历数组找到可检索的规格属性,通过过滤和映射把所有可检索的规格属性放进SkuEsModel类的内部类Attrs里,组成attrList的List,然后设置进esModel里

4.通过上述流程esModel里数据就充足了,我们通过远程调用gulimall-search服务,来将数据发给es保存

5.保存成功,修改当前spu的状态为 已上架

以下是部分代码:

复制代码
SpuInfoServiceImpl:主方法体:
复制代码
    @Override
    public void up(Long spuId) {
        //1.1查出当前spuid对应的所有sku信息,品牌的名字
        List<SkuInfoEntity> skus=skuInfoService.getSkusBySpuId(spuId);
        List<Long> skuIdList = skus.stream().map(SkuInfoEntity::getSkuId).collect(Collectors.toList());//拿到所有的skuId ,方便我们用id去查库存
        //TODO 4.查询当前SkU的所有可以被用来检索的规格属性
        List<ProductAttrValueEntity> baseAttrs = productAttrValueService.baseAttrListForSpu(spuId);//查出SKU的所有规格属性
        List<Long> attrIds = baseAttrs.stream().map(attr -> {
            return attr.getAttrId();
        }).collect(Collectors.toList());//把这些规格属性的ID组成一个数组

        List<Long> searchAttrIds= attrService.selectSearchAttrs(attrIds);//通过遍历这个规格属性的Id,找到 可检索 的 规格属性的Id

        Set<Long> idSet=new HashSet<>(searchAttrIds);//这里转成Set不是为了去重是因为set的查找操作时间复杂度是 O(1),而 List 的查找操作时间复杂度是 O(n)

        List<SkuEsModel.Attrs> attrsList = baseAttrs.stream().filter(item -> {//baseAttrs 里放的是规格属性的实体类,我们通过之前的查到的可检索的规格属性的ID,把实体类其中不可检索的过滤掉
            return idSet.contains(item.getAttrId());
        }).map(item -> {//再把 可检索的规格属性 映射成为SkuEsModel的内部类Attrs,方便我们底下的set
            SkuEsModel.Attrs attrs = new SkuEsModel.Attrs();
            BeanUtils.copyProperties(item, attrs);
            return attrs;
        }).collect(Collectors.toList());//这个stream流的功能是找出 baseAttrs 里的可检索的规格属性,装进 SkuEsModel的内部类Attrs(attrs) 里
        //TODO 1.发送远程调用,库存系统查询是否有库存 因为是批量所以拿出来
        Map<Long, Boolean> stockMap=null;
        try {
            R r = wareFeignService.getSkusHasStock(skuIdList);
            TypeReference<List<SkuHasStockVo>> typeReference = new TypeReference<>() {};
            stockMap = r.getData(typeReference).stream().collect(Collectors.toMap(SkuHasStockVo::getSkuId, item -> item.getHasStock()));
        }catch (Exception e){
            log.error("库存查询服务异常:原因{}",e);
        }
        //1.2封装每个sku的信息
        Map<Long, Boolean> finalStockMap = stockMap;
        List<SkuEsModel> upProducts = skus.stream().map(sku -> {
            //组装需要的数据
            SkuEsModel esModel = new SkuEsModel();
            BeanUtils.copyProperties(sku,esModel);
            esModel.setSkuPrice(sku.getPrice());
            esModel.setSkuImg(sku.getSkuDefaultImg());
            //TODO 1.发送远程调用,库存系统查询是否有库存

            //设置库存信息
            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进行保存  gulimall-search服务
        R r = searchFeignService.productStatusUp(upProducts);
        if(r.getCode()==0){
            //远程调用成功
            //TODO 6.修改当前SPU的状态
            baseMapper.updateSpuStatus(spuId, ProductConstant.StatusEnum.UP_SPU.getCode());
        }else {
            //远程调用失败
            //TODO 7.重复调用? 接口幂等性;重试机制?
            //feign的调用流程
            /**
             * 1.构造请求数据,将对象转为json
             * 2.发送请求进行执行(执行成功会解码相应的数据)
             * 3.执行请求会有重试机制
             */
        }
    }

wear查询是否有库存:

复制代码
   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==null?false:count>0);
            return vo;
        }).collect(Collectors.toList());
        return collect;
    }

统一返回类R里新增的方法:

复制代码
//利用fastjson进行逆转
	public <T> T getData(TypeReference<T> typeReference){
		Object data = get("data");
		String s = JSON.toJSONString(data);
		T t = JSON.parseObject(s, typeReference);
		return t;
	}
	public R setData(Object data){
		put("data",data);
		return this;
	}

gulimall-search的controller类:

复制代码
 //上架商品
    @PostMapping("/product")
    public R  productStatusUp(@RequestBody List<SkuEsModel> skuEsModels){
        boolean b=false;
        try {
            b=productSaveService.productStatusUp(skuEsModels);
        }catch (Exception e){
            log.error("ElasticSaveController商品上架错误:{}",e);
            return R.error(BizCodeEnume.PRODUCT_UP_EXCEPTION.getCode(),BizCodeEnume.PRODUCT_UP_EXCEPTION.getMsg());
        }
        if (!b){
            return R.ok();
        }else {
            return R.error(BizCodeEnume.PRODUCT_UP_EXCEPTION.getCode(),BizCodeEnume.PRODUCT_UP_EXCEPTION.getMsg());
        }
    }

方法的实现:

复制代码
    @Override
    public Boolean productStatusUp(List<SkuEsModel> skuEsModels) throws IOException {
        //保存进es
        //1.给ES中建立索引: product,建立好映射关系
        //2.给ES中保存数据:批量操作bulk
        BulkRequest bulkRequest = new BulkRequest();
        for (SkuEsModel skuEsModel : skuEsModels) {
            //构造保存请求
            IndexRequest indexRequest = new IndexRequest(EsConstant.PRODUCT_INDEX);
            indexRequest.id(skuEsModel.getSkuId().toString());
            String s = JSON.toJSONString(skuEsModel);
            indexRequest.source(s, XContentType.JSON);
            bulkRequest.add(indexRequest);
        }
        BulkResponse bulk = restHighLevelClient.bulk(bulkRequest, GulimallElasticSearchConfig.COMMON_OPTIONS);
        //TODO 如果批量错误 可以处理错误
        Boolean b=bulk.hasFailures();
        List<String> collect = Arrays.stream(bulk.getItems()).map(item -> {
            return item.getId();
        }).collect(Collectors.toList());
        log.info("商品上架完成:{}",collect);
        return b;
    }
相关推荐
YikNjy1 小时前
break和continue
java·开发语言·算法
SomeOtherTime1 小时前
Geojson相关(AI回答)
java·前端·python
日月云棠1 小时前
10 Integer —— 最常用的整数包装类深度解析
java·后端
秋91 小时前
java项目中cpu飙升排查及解决方法
java·开发语言
Elastic 中国社区官方博客1 小时前
我们如何在 Elasticsearch Serverless 上将向量搜索吞吐量提升一倍
大数据·数据库·人工智能·elasticsearch·搜索引擎·云原生·serverless
野生技术架构师1 小时前
牛客网2026最新大厂Java高频面试题精选(附标准答案)
java·开发语言
PH = 71 小时前
JAVA的SPI机制
java·开发语言
一 乐1 小时前
高校实习信息发布网站|基于Spring Boot的高校实习信息发布网站的设计与实现(源码+数据库+文档)
java·数据库·spring boot·后端·论文·毕设·高校实习信息发布网站
weelinking1 小时前
【产品】11_实现后端接口——数据在背后如何流动
java·人工智能·python·sql·oracle·json·ai编程
摇滚侠2 小时前
东方通替换tomcat,实战经验
java