商品上架之上传数据到Elasticsearch

  • 功能需求分析
    • 分析-怎么设计存储结构来保存数据
      • 空间换时间
      • 时间换空间
      • 最终方案-存储结构
        • 关于 nested 类型
  • 商品上架功能实现
    • guimall-common
      • pom.xml
      • com/xfwang/gulimall/common/to/es/SkuEsModel.java
      • com/xfwang/gulimall/common/exception/BizCodeEnume.java
      • com/xfwang/gulimall/common/constant/ProductConstant.java
      • com/xfwang/gulimall/common/vo/SkuHasStockVo.java
    • guimall-search
      • application.yml
      • com/xfwang/gulimall/search/constant/EsConstant.java
      • com/xfwang/gulimall/search/controller/ElasticSaveController.java
      • com/xfwang/gulimall/search/service/ProductSaveService.java
      • com/xfwang/gulimall/search/service/iml/ProductSaveServiceImpl.java
      • 设置启动类可以注册到nacos
    • guimall-ware
      • com/xfwang/gulimall/ware/controller/WareSkuController.java
      • com/xfwang/gulimall/ware/service/WareSkuService.java
      • com/xfwang/gulimall/ware/service/impl/WareSkuServiceImpl.java
      • com/xfwang/gulimall/ware/dao/WareSkuDao.java
      • mapper/ware/WareSkuDao.xml
    • guimall-product
      • com/xfwang/gulimall/product/controller/SpuInfoController.java
      • service
      • WareSkuDao
      • WareSkuDao.xml
      • openFeign
      • pom.xml

功能需求分析

  1. 需要保存 sku 信息
    当搜索商品名时,查询的是 sku 的标题 sku_title;
    可能通过 sku 的标题、销量、价格区间检索
  2. 需要保存品牌、分类等信息
    点击分类,检索分类下的所有信息
    点击品牌,检索品牌下的商品信息
  3. 需要保存 spu 信息
    选择规格,检索共有这些规格的商品

分析-怎么设计存储结构来保存数据

空间换时间

{skuId:1spuId:11skyTitile:华为xxprice:999saleCount:99attrs:[{尺寸:5存},{CPU:高通945},{分辨率:全高清}]
}
# 缺点:会产生冗余字段,对于相同类型的商品,attrs 属性字段会重复,空间占用大
# 好处:方便检索

时间换空间

sku索引
{skuId:1spuId:11
}
attr索引
{spuId:11attrs:[{尺寸:5寸},{CPU:高通945},{分辨率:全高清}]
}
# 缺点:选择公共属性attr时,会检索当前属性的所有商品分类,然后再查询当前商品分类的所有可能属性;
#        导致耗时长。
# 好处:空间利用率高

最终方案-存储结构

PUT product
{"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 类型

Object 数据类型的数组会被扁平化处理为一个简单的键与值的列表,即对象的相同属性会放到同一个数组中,在检索时会出现错误。参考官网:How arrays of objects are flattened
对于 Object 类型的数组,要使用 nested 字段类型。参考官网:Using nested fields for arrays of objects

官网

商品上架功能实现

guimall-common

我们在远程调用nacos的服务时出现ribbon的报错提示

我在pom.xml的文件中间

pom.xml

    <!--注册中心--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId><exclusions><exclusion><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-ribbon</artifactId></exclusion></exclusions></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-loadbalancer</artifactId><version>2.2.2.RELEASE</version></dependency>

封装了es的操作的model

com/xfwang/gulimall/common/to/es/SkuEsModel.java

@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;@Datapublic static class Attrs {private Long attrId;private String attrName;private String attrValue;}}

com/xfwang/gulimall/common/exception/BizCodeEnume.java

public enum BizCodeEnume {/*** 系统未知异常*/UNKNOW_EXCEPTION(10000, "系统未知异常"),PRODUCT_UP_EXCEPTION(11000,"商品上架错误"),/*** 参数校验错误*/VAILD_EXCEPTION(10001, "参数格式校验失败");private final int code;private final String msg;BizCodeEnume(int code, String msg) {this.code = code;this.msg = msg;}public int getCode() {return code;}public String getMsg() {return msg;}}

com/xfwang/gulimall/common/constant/ProductConstant.java

public class ProductConstant {public enum AttrEnum {ATTR_TYPE_BASE(1, "基本属性"), ATTR_TYPE_SALE(0, "销售属性");private int code;private String msg;AttrEnum(int code, String msg) {this.code = code;this.msg = msg;}public int getCode() {return code;}public String getMsg() {return msg;}}public enum StatusEnum {SPU_UP(1, "商品上架"), NEW_UP(0, "新建"),SPU_DOWN(2, "商品下架");private int code;private String msg;StatusEnum(int code, String msg) {this.code = code;this.msg = msg;}public int getCode() {return code;}public String getMsg() {return msg;}}
}

com/xfwang/gulimall/common/vo/SkuHasStockVo.java

@Data
public class SkuHasStockVo {private Long skuId;private Boolean hasStock;
}

首先es的模块加入注册到nacos服务,这样可以通过gateway网关调用服务

guimall-search

application.yml

spring:application:name: gulimall-searchcloud:nacos:discovery:server-addr: 123.57.234.28:8848server:port: 12000

com/xfwang/gulimall/search/constant/EsConstant.java

public class EsConstant {public static final  String PRODUCT_INDEX = "product";}

com/xfwang/gulimall/search/controller/ElasticSaveController.java

@Slf4j
@RestController
@RequestMapping("/search/save")
public class ElasticSaveController {@Autowiredprivate ProductSaveService productSaveService;/*** 上架商品*/@PostMapping("/product") // ElasticSaveControllerpublic R productStatusUp(@RequestBody List<SkuEsModel> skuEsModels){boolean status;try {status = productSaveService.productStatusUp(skuEsModels);} catch (IOException e) {log.error("ElasticSaveController商品上架错误: {}", e);return R.error(BizCodeEnume.PRODUCT_UP_EXCEPTION.getCode(), BizCodeEnume.PRODUCT_UP_EXCEPTION.getMsg());}if(!status){return R.ok();}return R.error(BizCodeEnume.PRODUCT_UP_EXCEPTION.getCode(), BizCodeEnume.PRODUCT_UP_EXCEPTION.getMsg());}}

com/xfwang/gulimall/search/service/ProductSaveService.java

public interface ProductSaveService {boolean productStatusUp(List<SkuEsModel> skuEsModels) throws IOException;
}

com/xfwang/gulimall/search/service/iml/ProductSaveServiceImpl.java

@Slf4j
@Service
public class ProductSaveServiceImpl implements ProductSaveService {@Autowiredprivate RestHighLevelClient client;@Overridepublic boolean productStatusUp(List<SkuEsModel> skuEsModels) throws IOException {// 1.给ES建立一个索引 productBulkRequest bulkRequest = new BulkRequest();// 2.构造保存请求for (SkuEsModel esModel : skuEsModels) {// 设置索引IndexRequest indexRequest = new IndexRequest(EsConstant.PRODUCT_INDEX);// 设置索引idindexRequest.id(esModel.getSkuId().toString());String jsonString = JSON.toJSONString(esModel);indexRequest.source(jsonString, XContentType.JSON);// addbulkRequest.add(indexRequest);}// bulk批量保存BulkResponse bulk = client.bulk(bulkRequest, EsConfig.COMMON_OPTIONS);// TODO 是否拥有错误boolean hasFailures = bulk.hasFailures();if(hasFailures){List<String> collect = Arrays.stream(bulk.getItems()).map(item -> item.getId()).collect(Collectors.toList());log.error("商品上架错误:{}",collect);}return hasFailures;}}

设置启动类可以注册到nacos

GulimallSearchApplication
@EnableDiscoveryClient

@SpringBootApplication(exclude={DataSourceAutoConfiguration.class})
@EnableDiscoveryClient
public class GulimallSearchApplication {public static void main(String[] args) {SpringApplication.run(GulimallSearchApplication.class, args);}}

guimall-ware

查询库存方法通过openFeign远程调用

com/xfwang/gulimall/ware/controller/WareSkuController.java

@RestController
@RequestMapping("ware/waresku")
public class WareSkuController {@Autowiredprivate WareSkuService wareSkuService;@PostMapping("/hasStock")public  List<SkuHasStockVo> getSkuHasStock(@RequestBody List<Long> SkuIds){List<SkuHasStockVo> vos = wareSkuService.getSkuHasStock(SkuIds);return vos;//return R.ok().put("data",vos);}...
}

com/xfwang/gulimall/ware/service/WareSkuService.java

public interface WareSkuService extends IService<WareSkuEntity> {PageUtils queryPage(Map<String, Object> params);/*** 保存库存的时候顺便查到商品价格*/double addStock(Long skuId, Long wareId, Integer skuNum);List<SkuHasStockVo> getSkuHasStock(List<Long> skuIds);
}

com/xfwang/gulimall/ware/service/impl/WareSkuServiceImpl.java


@Service("wareSkuService")
public class WareSkuServiceImpl extends ServiceImpl<WareSkuDao, WareSkuEntity> implements WareSkuService {...@Overridepublic List<SkuHasStockVo> getSkuHasStock(List<Long> skuIds) {List<SkuHasStockVo> collect = skuIds.stream().map(skuId -> {SkuHasStockVo vo = new SkuHasStockVo();//查询库存的总库存量Long count = baseMapper.getSkuStock(skuId);vo.setHasStock(count== null ?true:count>0 );vo.setSkuId(skuId);return vo;}).collect(Collectors.toList());return collect;}
}

com/xfwang/gulimall/ware/dao/WareSkuDao.java

@Mapper
public interface WareSkuDao extends BaseMapper<WareSkuEntity> {void addStock(@Param("skuId") Long skuId, @Param("wareId") Long wareId, @Param("skuNum") Integer skuNum);Long getSkuStock(Long skuId);
}

mapper/ware/WareSkuDao.xml

<mapper namespace="com.xfwang.gulimall.ware.dao.WareSkuDao">...<select id="getSkuStock" resultType="java.lang.Long">SELECTSUM(stock - stock_locked) countFROMwms_ware_skuWHEREsku_id = #{skuId}</select></mapper>

guimall-product

上架功能主要通过 product远程调用其他服务来实现

com/xfwang/gulimall/product/controller/SpuInfoController.java

@RestController
@RequestMapping("product/spuinfo")
public class SpuInfoController {@Autowiredprivate SpuInfoService spuInfoService;/*** 商品上架功能** @param spuId* @return*/@PostMapping("/{spuId}/up")public R upSpu(@PathVariable Long spuId) {spuInfoService.up(spuId);return R.ok();}...}

service

AttrService

public interface AttrService extends IService<AttrEntity> {...List<Long> selectSearchAttrIds(List<Long> attrIds);
}

AttrServiceImpl

@Service("attrService")
public class AttrServiceImpl extends ServiceImpl<AttrDao, AttrEntity> implements AttrService {...@Overridepublic List<Long> selectSearchAttrIds(List<Long> attrIds) {QueryWrapper<AttrEntity> queryWrapper = new QueryWrapper<>();queryWrapper.lambda().select(AttrEntity::getAttrId).in(AttrEntity::getAttrId,attrIds).eq(AttrEntity::getSearchType,"1");List<Long> collect = list(queryWrapper).stream().map(item -> item.getAttrId()).collect(Collectors.toList());return collect;}
}

SkuInfoService

public interface SkuInfoService extends IService<SkuInfoEntity> {...List<SkuInfoEntity> getSkusBySpuId(Long spuId);
}

SkuInfoServiceImpl

@Service("skuInfoService")
public class SkuInfoServiceImpl extends ServiceImpl<SkuInfoDao, SkuInfoEntity> implements SkuInfoService {...@Overridepublic List<SkuInfoEntity> getSkusBySpuId(Long spuId) {List<SkuInfoEntity> list = this.list(new QueryWrapper<SkuInfoEntity>().eq("spu_id",spuId));return list;}
}

SpuInfoService

public interface SpuInfoService extends IService<SpuInfoEntity> {...void up(Long spuId);
}

SpuInfoServiceImpl

@Service("spuInfoService")
public class SpuInfoServiceImpl extends ServiceImpl<SpuInfoDao, SpuInfoEntity> implements SpuInfoService {...
@Overridepublic void up(Long spuId) {//1、查出当前spuId对应的所有sku信息,品牌的名字List<SkuInfoEntity> skuInfoEntities=skuInfoService.getSkusBySpuId(spuId);//TODO 4、根据spu查出当前sku的所有可以被用来检索的规格属性List<ProductAttrValueEntity> productAttrValueEntities = productAttrValueService.list(new QueryWrapper<ProductAttrValueEntity>().eq("spu_id", spuId));List<Long> attrIds = productAttrValueEntities.stream().map(attr -> {return attr.getAttrId();}).collect(Collectors.toList());List<Long> searchIds=attrService.selectSearchAttrIds(attrIds);Set<Long> ids = new HashSet<>(searchIds);List<SkuEsModel.Attrs> searchAttrs = productAttrValueEntities.stream().filter(entity -> {return ids.contains(entity.getAttrId());}).map(entity -> {SkuEsModel.Attrs attr = new SkuEsModel.Attrs();BeanUtils.copyProperties(entity, attr);return attr;}).collect(Collectors.toList());//TODO 1、发送远程调用,库存系统查询是否有库存Map<Long, Boolean> stockMap = null;try {List<Long> longList = skuInfoEntities.stream().map(SkuInfoEntity::getSkuId).collect(Collectors.toList());List<SkuHasStockVo> skuHasStocks = wareFeignService.getSkuHasStock(longList);stockMap = skuHasStocks.stream().collect(Collectors.toMap(SkuHasStockVo::getSkuId, SkuHasStockVo::getHasStock));}catch (Exception e){log.error("远程调用库存服务失败,原因{}",e);}//2、封装每个sku的信息Map<Long, Boolean> finalStockMap = stockMap;List<SkuEsModel> skuEsModels = skuInfoEntities.stream().map(sku -> {SkuEsModel skuEsModel = new SkuEsModel();BeanUtils.copyProperties(sku, skuEsModel);skuEsModel.setSkuPrice(sku.getPrice());skuEsModel.setSkuImg(sku.getSkuDefaultImg());//TODO 2、热度评分。0skuEsModel.setHotScore(0L);//TODO 3、查询品牌和分类的名字信息BrandEntity brandEntity = brandService.getById(sku.getBrandId());skuEsModel.setBrandName(brandEntity.getName());skuEsModel.setBrandImg(brandEntity.getLogo());CategoryEntity categoryEntity = categoryService.getById(sku.getCatalogId());skuEsModel.setCatalogName(categoryEntity.getName());//设置可搜索属性skuEsModel.setAttrs(searchAttrs);//设置是否有库存skuEsModel.setHasStock(finalStockMap==null?false:finalStockMap.get(sku.getSkuId()));return skuEsModel;}).collect(Collectors.toList());//TODO 5、将数据发给es进行保存:gulimall-searchR r = searchFeignService.productStatusUp(skuEsModels);if (r.getCode()==0){this.baseMapper.upSpuStatus(spuId, ProductConstant.StatusEnum.SPU_UP.getCode());}else {log.error("商品远程es保存失败");}}
}

WareSkuDao

@Mapper
public interface WareSkuDao extends BaseMapper<WareSkuEntity> {void addStock(@Param("skuId") Long skuId, @Param("wareId") Long wareId, @Param("skuNum") Integer skuNum);Long getSkuStock(Long skuId);
}

WareSkuDao.xml

<mapper namespace="com.xfwang.gulimall.ware.dao.WareSkuDao">...<select id="getSkuStock" resultType="java.lang.Long">SELECTSUM(stock - stock_locked) countFROMwms_ware_skuWHEREsku_id = #{skuId}</select></mapper>

openFeign

SearchFeignService

@FeignClient("gulimall-search")
public interface SearchFeignService {@PostMapping("/search/save/product") // ElasticSaveControllerR productStatusUp(@RequestBody List<SkuEsModel> skuEsModels);
}

WareFeignService

@FeignClient("gulimall-ware")
public interface WareFeignService {@PostMapping("/ware/waresku/hasStock")List<SkuHasStockVo> getSkuHasStock(@RequestBody List<Long> SkuIds);
}

pom.xml

 <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId><exclusions><exclusion><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-ribbon</artifactId></exclusion></exclusions></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-loadbalancer</artifactId></dependency>

谷粒商城高级篇(36)——商品上架之上传数据到Elasticsearch相关推荐

  1. 【谷粒商城高级篇】商品服务 商品上架

    谷粒商城笔记合集 分布式基础篇 分布式高级篇 高可用集群篇 ===简介&环境搭建=== ===Elasticsearch=== 项目简介与分布式概念(第一.二章) Elasticsearch: ...

  2. 谷粒商城高级篇上(未完待续)

    谷粒商城高级篇(上)保姆级整理 之前整理了基础篇,Typora提示将近20000词,谷粒商城基础篇保姆级整理 在学高级篇的时候,不知不觉又整理了两万多词,做了一阶段,先发出来,剩余部分整理好了再发.自 ...

  3. 【谷粒商城高级篇】商城业务:商品检索

    谷粒商城笔记合集 分布式基础篇 分布式高级篇 高可用集群篇 ===简介&环境搭建=== ===Elasticsearch=== 项目简介与分布式概念(第一.二章) Elasticsearch: ...

  4. 谷粒商城高级篇(38)——异步编排之商品详情查询

    异步编排之商品详情查询 异步编排 CompletableFuture介绍 创建异步对象 计算完成时回调方法 handle 方法 线程串行化方法 两任务组合 全部完成 一个完成即可 多任务组合 业务描述 ...

  5. 【谷粒商城高级篇】Elasticsearch:全文检索

    谷粒商城笔记合集 分布式基础篇 分布式高级篇 高可用集群篇 ===简介&环境搭建=== ===Elasticsearch=== 项目简介与分布式概念(第一.二章) Elasticsearch: ...

  6. 谷粒商城高级篇笔记1

    这里写自定义目录标题 0.ElasticSearch 1.Nginx配置域名问题 01.Nginx(反向代理) 配置 02.Nginx(负载均衡)+ 网关 配置 03.Nginx动静分离 2.JMet ...

  7. 谷粒商城-高级篇-aiueo

    105 初步检索 105.1 _cat GET /_cat/nodes : 查看所有节点 GET /_cat/health : 查看es健康状况 GET /_cat/master : 查看主节点 GE ...

  8. 谷粒商城高级篇资料_一文搞定剑指offer面试题【分文别类篇】

    点击上方"蓝字",关注了解更多 数组: 面试题3:数组中重复的数字 面试题4:二维数组中的查找 面试题21:调整数组顺序使奇数位于偶数前面 面试题39:数组中出现次数超过一半的数字 ...

  9. 谷粒商城高级篇爬坑笔记--错误异常信息乱码问题

    由于高级篇开发相对较多,配置的内容较少,本人编写过程中没有遇到特别大的问题,唯一的问题就是消息乱码: 项目定义了如下的异常类: UNKNOW_EXCEPTION(10000,"系统未知异常& ...

最新文章

  1. 医疗人工智能会替代医生吗
  2. 杨辉三角(下三角或者等腰三角)
  3. 2021算法竞赛入门班第十节课【字符串】练习题
  4. boost::describe模块实现string转enum的测试程序
  5. Cocos2d-x V3.2+Cocos Studio1.6 实现一个简单的uibutton点击功能
  6. nodejs连接redis,redis服务器的地址格式应该怎么写
  7. 识别Gradle约定
  8. Windows10+Ubuntu 18.04.2+ROS 安装笔记(SSD单硬盘)上
  9. 爱的十个秘密--10.热情的力量
  10. MySQL DATE_ADD() 函数
  11. Observable与Observer
  12. 随记:Linux下修改网络配置
  13. ACM学习历程—HDU1584 蜘蛛牌(动态规划 状态压缩 || 区间DP)
  14. 正则表达式功能以及应用
  15. 通过OpenSSL创建自签名证书在Flask实现HTTPS
  16. 51单片机入门——8X8点阵LED
  17. 超全面!完全没有设计基础的新手如何做好PPT配色?(附神器)
  18. 机器学习之逻辑回归(Logistic Regression)
  19. matlab高尔顿板钉试验,高尔顿钉板试验动态图形软件的设计与制作
  20. 浅析微信支付:公众平台卡券功能开通、HTML5线上发券(JS-SDK接口)、查看卡券详情

热门文章

  1. Linux进程调度策略的发展和演变--Linux进程的管理与调度(十六)
  2. etl工程师 面试题_关于数据仓库工程师的一般面试题目
  3. 待办日程用什么软件好 2022好用的便签记事日程管理软件推荐
  4. robotstudio试用期延长总结
  5. 第五届 蓝桥杯 海盗分金币 C语言
  6. uniapp开发微信小程序生成二维码海报
  7. android 支付sdk流程,支付SDK
  8. x*=3+5**2的计算结果python_下面代码的执行结果是________ x = 2 x *= 3 + 5**2
  9. 微信公众平台开发尝试
  10. selenium 模拟IE浏览器click元素无反应的解决方案 (python3)