谷粒商城高级篇(36)——商品上架之上传数据到Elasticsearch
商品上架之上传数据到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
功能需求分析
- 需要保存 sku 信息
当搜索商品名时,查询的是 sku 的标题 sku_title;
可能通过 sku 的标题、销量、价格区间检索 - 需要保存品牌、分类等信息
点击分类,检索分类下的所有信息
点击品牌,检索品牌下的商品信息 - 需要保存 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相关推荐
- 【谷粒商城高级篇】商品服务 商品上架
谷粒商城笔记合集 分布式基础篇 分布式高级篇 高可用集群篇 ===简介&环境搭建=== ===Elasticsearch=== 项目简介与分布式概念(第一.二章) Elasticsearch: ...
- 谷粒商城高级篇上(未完待续)
谷粒商城高级篇(上)保姆级整理 之前整理了基础篇,Typora提示将近20000词,谷粒商城基础篇保姆级整理 在学高级篇的时候,不知不觉又整理了两万多词,做了一阶段,先发出来,剩余部分整理好了再发.自 ...
- 【谷粒商城高级篇】商城业务:商品检索
谷粒商城笔记合集 分布式基础篇 分布式高级篇 高可用集群篇 ===简介&环境搭建=== ===Elasticsearch=== 项目简介与分布式概念(第一.二章) Elasticsearch: ...
- 谷粒商城高级篇(38)——异步编排之商品详情查询
异步编排之商品详情查询 异步编排 CompletableFuture介绍 创建异步对象 计算完成时回调方法 handle 方法 线程串行化方法 两任务组合 全部完成 一个完成即可 多任务组合 业务描述 ...
- 【谷粒商城高级篇】Elasticsearch:全文检索
谷粒商城笔记合集 分布式基础篇 分布式高级篇 高可用集群篇 ===简介&环境搭建=== ===Elasticsearch=== 项目简介与分布式概念(第一.二章) Elasticsearch: ...
- 谷粒商城高级篇笔记1
这里写自定义目录标题 0.ElasticSearch 1.Nginx配置域名问题 01.Nginx(反向代理) 配置 02.Nginx(负载均衡)+ 网关 配置 03.Nginx动静分离 2.JMet ...
- 谷粒商城-高级篇-aiueo
105 初步检索 105.1 _cat GET /_cat/nodes : 查看所有节点 GET /_cat/health : 查看es健康状况 GET /_cat/master : 查看主节点 GE ...
- 谷粒商城高级篇资料_一文搞定剑指offer面试题【分文别类篇】
点击上方"蓝字",关注了解更多 数组: 面试题3:数组中重复的数字 面试题4:二维数组中的查找 面试题21:调整数组顺序使奇数位于偶数前面 面试题39:数组中出现次数超过一半的数字 ...
- 谷粒商城高级篇爬坑笔记--错误异常信息乱码问题
由于高级篇开发相对较多,配置的内容较少,本人编写过程中没有遇到特别大的问题,唯一的问题就是消息乱码: 项目定义了如下的异常类: UNKNOW_EXCEPTION(10000,"系统未知异常& ...
最新文章
- 医疗人工智能会替代医生吗
- 杨辉三角(下三角或者等腰三角)
- 2021算法竞赛入门班第十节课【字符串】练习题
- boost::describe模块实现string转enum的测试程序
- Cocos2d-x V3.2+Cocos Studio1.6 实现一个简单的uibutton点击功能
- nodejs连接redis,redis服务器的地址格式应该怎么写
- 识别Gradle约定
- Windows10+Ubuntu 18.04.2+ROS 安装笔记(SSD单硬盘)上
- 爱的十个秘密--10.热情的力量
- MySQL DATE_ADD() 函数
- Observable与Observer
- 随记:Linux下修改网络配置
- ACM学习历程—HDU1584 蜘蛛牌(动态规划 状态压缩 || 区间DP)
- 正则表达式功能以及应用
- 通过OpenSSL创建自签名证书在Flask实现HTTPS
- 51单片机入门——8X8点阵LED
- 超全面!完全没有设计基础的新手如何做好PPT配色?(附神器)
- 机器学习之逻辑回归(Logistic Regression)
- matlab高尔顿板钉试验,高尔顿钉板试验动态图形软件的设计与制作
- 浅析微信支付:公众平台卡券功能开通、HTML5线上发券(JS-SDK接口)、查看卡券详情
热门文章
- Linux进程调度策略的发展和演变--Linux进程的管理与调度(十六)
- etl工程师 面试题_关于数据仓库工程师的一般面试题目
- 待办日程用什么软件好 2022好用的便签记事日程管理软件推荐
- robotstudio试用期延长总结
- 第五届 蓝桥杯 海盗分金币 C语言
- uniapp开发微信小程序生成二维码海报
- android 支付sdk流程,支付SDK
- x*=3+5**2的计算结果python_下面代码的执行结果是________ x = 2 x *= 3 + 5**2
- 微信公众平台开发尝试
- selenium 模拟IE浏览器click元素无反应的解决方案 (python3)