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

商品上架之上传数据到Elasticsearch

  • ​​功能需求分析​​
  • ​​分析-怎么设计存储结构来保存数据​​
    • ​​空间换时间​​
    • ​​时间换空间​​
    • ​​最终方案-存储结构​​
      • ​​关于 nested 类型​​
  • ​​商品上架功能实现​​
  • ​​guimall-common​​
    • ​​pom.xml​​
    • ​​com/xfwang/gulimall/common/to/es/SkuEs小米路由器登录入口Model.java​​
    • ​​com/xfwang/gulimall/common/exception/BizCodeEnume.java​​
    • ​​com/xfwang/gullinux除文件命令imalinuxll/common/constant/ProductConstanspringcloud五大组件t.java​​
    • ​​com/xfwang/gulimall/common/vo/SkuHasStockVo.java​​
  • ​​guimall-search​​
    • ​​application.yml​​
    • ​​com/xfwang/gulimall/search/cjava模拟器onstant/EsCojava编译器nstalinux操作系统基础知识nt.java培训java​​
    • ​​com/xfwa小毛驴简谱ng/gulimall/search/controller/spring框架ElasticSaveController.java​​
    • ​​com/xfwang/gulimall/search/service/ProductSaveService.java​​
    • ​​com/xfwang/gulimajava培训ll/search/service/iml/ProductSaveServiceImpl.java​​
    • ​​设置启动类可以注册到nacos​​
  • ​​guimspring是什么意思all-ware​​
    • ​​com/xfwang/gulimall/war系统运维工程师e/controller/WareSkuController.java​​
    • ​​com/xfwjava怎么读ang/gulimall/ware/service/WareSkuService.java​​
    • ​​com/xfwang/gulimall/ware/serv小米路由器ice/impl/WareSkuServiceImpl.java​​
    • ​​com/xfwang/gulimall/ware/dao/WareSkuDao.java​​
    • ​​mapper/ware/WareSkuDao.xml​​
  • ​​guimall-product​java环境变量配置
    • ​​com/xfwang/gulimall/product/controller/SpuInfoController.java​​
    • ​​service​​
    • ​​WareSkuDao​​
    • ​​WareSkuDao.xml​​
    • ​​openFeign​​
    • ​​pom.xml​​

功能需求分析

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

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

空间换时间

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

时间换空间

sku索引
{
skuId:1
spuId:11
}
attr索引
{
spuId:11
attrs:[
{尺寸: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" }
}
}
}
}
}

springfield于 nested 类型

Object 数据类型的数组会被扁平化处理为一个简单的键与值的列表,即对象的相同属性会放springcloud五大组件到同一个数组中,在检索时会出现错误。参考官网:How arrays of objects are flattened

对于 Object 类型的数组,要使用 nested 字段类型。参考官网:Usi西门龙霆ng 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;
@Data
public static class Attrs {
private Long attrId;
private String attrName;
private String attrValue;
}


}

com/xfwang/gulimall/common/exception/BizCodeE系统运维工资一般多少nume.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.ja系统运维工作内容va

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-sspringcloud五大组件earch

application.yml

spring:
application:
name: gulimall-search
cloud:
nacos:
discovery:
server-addr: 123.57.234.28:8848


server:
port: 12000

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

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


}

com喜马拉雅听书免费版官方下载/xfwang/gulimall/search/controller/ElastspringbooticSaveController.java

@Slf4j
@RestController
@RequestMapping("/search/save")
public class ElasticSaveController {


@Autowired
private ProductSaveService productSaveService;

/**
* 上架商品
*/
@PostMapping("/product") // ElasticSaveController
public 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/linux除文件命令xfwang/gulimall/search/sjavascript百炼成仙ervice/ProductSavelinux必学的60个命令Service.jav小米路由器登录入口a

public interface ProductSaveService {

boolean productStatusUp(List<SkuEsModel> skuEsModels) throws IOException;
}

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

@Slf4j
@Service
public class ProductSaveServiceImpl implements ProductSaveService {


@Autowired
private RestHighLevelClient client;


@Override
public boolean productStatusUp(List<SkuEsModel> skuEsModels) throws IOException {
// 1.给ES建立一个索引 product
BulkRequest bulkRequest = new BulkRequest();
// 2.构造保存请求
for (SkuEsModel esModel : skuEsModels) {
// 设置索引
IndexRequest indexRequest = new IndexRequest(EsConstant.PRODUCT_INDEX);
// 设置索引id
indexRequest.id(esModel.getSkuId().toString());
String jsonString = JSON.toJSONString(esModel);
indexRequest.source(jsonString, XContentType.JSON);
// add
bulkRequest.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

Gulimallspringboot面试题SearchApplication

@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/linux删除文件命令controller/WareSkuController.java

@RestController
@RequestMapping("ware/waresku")
public class WareSkuController {
@Autowired
private 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/xfwanglinux删除文件命令/gulimal系统运维工程师面试问题及答案l/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 {
...
@Override
public 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/xfwaspring是什么意思ng/gspringcloud五大组件ulimall/ware/dao/WareSkuDajavaeeo.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/WarespringcloudSkuDao.xml

<mapper namespace="com.xfwang.gulimall.ware.dao.WareSkuDao">
...

<select id="getSkuStock" resultType="java.lang.Long">
SELECT
SUM(stock - stock_locked) count
FROM
wms_ware_sku
WHERE
sku_id = #{skuId}
</select>


</mapper>

gui喜马拉雅mall-pr星门老鹰oduclinux重启命令t

系统/运维架功能主要java怎么读通过 product远程调用其他服务来实现

com/xfwang/gulimallspring/product喜马拉雅/controller/SpuInfoController.javlinuxa

@RestController
@RequestMapping("product/spuinfo")
public class SpuInfoController {

@Autowired
private SpuInfoService spuInfoService;


/**
* 商品上架功能
*
* @param spuId
* @return
*/
@PostMapping("/{spuId}/up")
public R upSpu(@PathVariable Long spuId) {
spuInfoService.up(spuId);
return R.ok();
}
...
}

service

AttrServspringcloudice

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

AttrSe系统运维面试题及答案rviceImpl

@Service("attrService")
public class AttrServiceImpl extends ServiceImpl<AttrDao, AttrEntity> implements AttrService {
...
@Override
public 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 {
...
@Override
public List<SkuInfoEntity> getSkusBySpuId(Long spuId) {

List<SkuInfoEntity> list = this.list(new QueryWrapper<SkuInfoEntity>().eq("spu_id",spuId));
return list;
}
}

SpuInfoSe迅猛龙rvice

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

SpuInfoServiceImpl

@Service("spuInfoService")
public class SpuInfoServiceImpl extends ServiceImpl<SpuInfoDao, SpuInfoEntity> implements SpuInfoService {
...
@Override
public 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、热度评分。0
skuEsModel.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-search
R 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">
SELECT
SUM(stock - stock_locked) count
FROM
wms_ware_sku
WHERE
sku_id = #{skuId}
</select>


</mapper>

openFeign

SearchFejavaeeignService

@FeignClient("gulimall-search")
public interface SearchFeignService {


@PostMapping("/search/save/product") // ElasticSaveController
R 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>