学习目标:
无论PC端/移动端,根据用户输入的检索条件,查询出对用的商品
首页的分类,按照三级分类ID进行查询
搜索栏,用户录入任意购买意向商品关键字进行检索
MySQL | ElasticSearch | |
---|---|---|
数据库 | database | index |
表 | table | type(已废弃) |
字段约束 | schema | mapping映射 |
行 | row | Document(文档) |
字段 | column | Field |
设计索引库方法:
这时我们要思考三个问题:
哪些字段需要分词
我们用哪些字段进行过滤(过滤项)
平台属性值
分类Id
品牌Id
哪些字段我们需要通过搜索查询出来(业务数据)。
以上分析的所有显示,以及分词,过滤的字段都应该在es中出现。Es中如何保存这些数据呢?
根据上述的字段描述,应该建立一个mappings对应的存上上述字段描述的信息!
根据以上制定出如下结构:mappings
Index:goods
document: properties
field: id,price,title…
ES中index默认是true。
注意:ik_max_word 中文词库必须有!
attrs:平台属性值的集合,主要用于平台属性值过滤。
字符串类型ES中两种:
nested:类型是一种特殊的对象object数据类型(specialised version of the object datatype ),允许对象数组彼此独立地进行索引和查询。
demo: 建立一个普通的index
如果linux 中有这个my_comment_index 先删除!DELETE /my_comment_index
步骤1:建立一个索引( 存储博客文章及其所有评论)
PUT my_comment_index/_doc/1
{
"title": "狂人日记",
"body": "《狂人日记》是一篇象征性和寓意很强的小说,当时,鲁迅对中国国民精神的麻木愚昧颇感痛切。",
"comments": [
{
"name": "张三",
"age": 34,
"rating": 8,
"comment": "非常棒的文章",
"commented_on": "30 Nov 2023"
},
{
"name": "李四",
"age": 38,
"rating": 9,
"comment": "文章非常好",
"commented_on": "25 Nov 2022"
},
{
"name": "王五",
"age": 33,
"rating": 7,
"comment": "手动点赞",
"commented_on": "20 Nov 2021"
}
]
}
#类比项目中商品信息
PUT goods/_doc/1
{
"title": "HUAWEI Mate 50 直屏旗舰 超光变XMAGE影像 北斗卫星消息 低电量应急模式 128GB冰霜银华为鸿蒙手机",
"attrInfo": [
{
"id": 23,
"attrName": "运行内存",
"attrValue": "8G"
},
{
"id": 114,
"attrName": "CPU型号",
"attrValue": "骁龙8+ Gen 1"
}
]
}
如上所示,所以我们有一个文档描述了一个帖子和一个包含帖子上所有评论的内部对象评论。 但是Elasticsearch搜索中的内部对象并不像我们期望的那样工作。
步骤2 : 执行查询
GET /my_comment_index/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"comments.name": "李四"
}
},
{
"match": {
"comments.age": 34
}
}
]
}
}
}
查询结果:居然正常的响应结果了
原因分析:comments字段默认的数据类型是Object,故我们的文档内部存储为:
{ "title": [ 狂人日记], "body": [ 《狂人日记》是一篇象征性和寓意很强的小说,当时... ], "comments.name": [ 张三, 李四, 王五 ], "comments.comment": [ 非常棒的文章,文章非常好,王五,... ], "comments.age": [ 33, 34, 38 ], "comments.rating": [ 7, 8, 9 ] }
{
"title": "HUAWEI Mate 50 直屏旗舰 超光变XMAGE影像 北斗卫星消息 低电量应急模式 128GB冰霜银华为鸿蒙手机",
"attrInfo.id":[23,114],
"attrInfo.attrName":["运行内存","CPU型号"],
"attrInfo.attrValue":["8G","CPU型号","骁龙8+ Gen 1"],
}
我们可以清楚地看到,comments.name和comments.age之间的关系已丢失。这就是为什么我们的文档匹配李四和34的查询。
步骤3:删除当前索引
DELETE /my_comment_index
步骤4:建立一个nested 类型的(comments字段映射为nested类型,而不是默认的object类型)
PUT my_comment_index
{
"mappings": {
"properties": {
"comments": {
"type": "nested"
}
}
}
}
PUT my_comment_index/_doc/1
{
"title": "狂人日记",
"body": "《狂人日记》是一篇象征性和寓意很强的小说,当时,鲁迅对中国国民精神的麻木愚昧颇感痛切。",
"comments": [
{
"name": "张三",
"age": 34,
"rating": 8,
"comment": "非常棒的文章",
"commented_on": "30 Nov 2023"
},
{
"name": "李四",
"age": 38,
"rating": 9,
"comment": "文章非常好",
"commented_on": "25 Nov 2022"
},
{
"name": "王五",
"age": 33,
"rating": 7,
"comment": "手动点赞",
"commented_on": "20 Nov 2021"
}
]
}
重新执行步骤1,使用nested 查询
GET /my_comment_index/_search
{
"query": {
"nested": {
"path": "comments",
"query": {
"bool": {
"must": [
{
"match": {
"comments.name": "李四"
}
},
{
"match": {
"comments.age": 34
}
}
]
}
}
}
}
}
结果发现没有返回任何的文档,这是何故?
当将字段设置为nested 嵌套对象将数组中的每个对象索引为单独的隐藏文档,这意味着可以独立于其他对象查询每个嵌套对象。文档的内部表示:
{ {
"comments.name": [ 张三], "comments.comment": [ 非常棒的文章 ], "comments.age": [ 34 ], "comments.rating": [ 9 ]
}, {
"comments.name": [ 李四], "comments.comment": [ 文章非常好 ], "comments.age": [ 38 ], "comments.rating": [ 8 ]
}, {
"comments.name": [ 王五], "comments.comment": [手动点赞], "comments.age": [ 33 ], "comments.rating": [ 7 ]
}, {
"title": [ 狂人日记 ], "body": [ 《狂人日记》是一篇象征性和寓意很强的小说,当时,鲁迅对中国... ]
} }
{
"title": "HUAWEI Mate 50 直屏旗舰 超光变XMAGE影像 北斗卫星消息 低电量应急模式 128GB冰霜银华为鸿蒙手机",
{
"attrInfo.id":[23],
"attrInfo.attrName":["运行内存"],
"attrInfo.attrValue":["8G"]
},
{
"attrInfo.id":[114],
"attrInfo.attrName":["CPU型号"],
"attrInfo.attrValue":["骁龙8 gen 1"]
}
}
每个内部对象都在内部存储为单独的隐藏文档。 这保持了他们的领域之间的关系。
在gmall-service
模块下搭建搜索模块:service-list
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>gmall-service</artifactId>
<groupId>com.atguigu.gmall</groupId>
<version>1.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>service-list</artifactId>
<dependencies>
<dependency>
<groupId>com.atguigu.gmall</groupId>
<artifactId>service-product-client</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
</dependencies>
</project>
说明:
引入service-product-client模块
引入spring-boot-starter-data-elasticsearch依赖
在父工程中gmall-parent
模块pom.xml中properties节点中指定client版本跟ES服务端版本一致
<elasticsearch.version>7.8.0</elasticsearch.version>
package com.atguigu.gmall;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@EnableDiscoveryClient
@EnableFeignClients
public class ListApp {
public static void main(String[] args) {
SpringApplication.run(ListApp.class, args);
}
}
bootstrap.properties
spring.application.name=service-list
spring.profiles.active=dev
spring.cloud.nacos.discovery.server-addr=192.168.200.128:8848
spring.cloud.nacos.config.server-addr=192.168.200.128:8848
spring.cloud.nacos.config.prefix=${spring.application.name}
spring.cloud.nacos.config.file-extension=yaml
spring.cloud.nacos.config.shared-configs[0].data-id=common.yaml
说明:Nacos配置文件中添加es配置信息
说明:在gmall-model
模块中已有商品文档实体类,以及平台属性实体类跟ES索引库mapping简历映射
商品文档实体类
package com.atguigu.gmall.list.model;
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.DateFormat;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
import java.util.Date;
import java.util.List;
import java.util.Objects;
// Index = goods , Type = info es 7.8.0 逐渐淡化type! 修改!
// es 的分片,副本是为了保证高可用!
@Data
@Document(indexName = "goods" , shards = 3,replicas = 2)
public class Goods {
// 商品Id skuId
@Id
private Long id;
@Field(type = FieldType.Keyword, index = false)
private String defaultImg;
// es 中能分词的字段,这个字段数据类型必须是 text!
@Field(type = FieldType.Text, analyzer = "ik_max_word")
private String title;
@Field(type = FieldType.Double)
private Double price;
// @Field(type = FieldType.Date) 6.8.1
@Field(type = FieldType.Date,format = DateFormat.custom,pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime; // 新品
@Field(type = FieldType.Long)
private Long tmId;
@Field(type = FieldType.Keyword)
private String tmName;
@Field(type = FieldType.Keyword)
private String tmLogoUrl;
@Field(type = FieldType.Long)
private Long category1Id;
@Field(type = FieldType.Keyword)
private String category1Name;
@Field(type = FieldType.Long)
private Long category2Id;
@Field(type = FieldType.Keyword)
private String category2Name;
@Field(type = FieldType.Long)
private Long category3Id;
@Field(type = FieldType.Keyword)
private String category3Name;
// 商品的热度! 我们将商品被用户点查看的次数越多,则说明热度就越高!
@Field(type = FieldType.Long)
private Long hotScore = 0L;
// 平台属性集合对象
// Nested 支持嵌套查询 允许对象数组彼此独立检索和查询
@Field(type = FieldType.Nested)
private List<SearchAttr> attrs;
}
销售属性实体类
package com.atguigu.gmall.model.list;
import lombok.Data;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
@Data
public class SearchAttr {
// 平台属性Id
@Field(type = FieldType.Long)
private Long attrId;
// 平台属性值名称
@Field(type = FieldType.Keyword)
private String attrValue;
// 平台属性名
@Field(type = FieldType.Keyword)
private String attrName;
}
package com.atguigu.gmall.list.controller;
import com.atguigu.gmall.common.result.Result;
import com.atguigu.gmall.list.model.Goods;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author: atguigu
* @create: 2023-03-03 10:10
*/
@RestController
@RequestMapping("api/list")
public class ListApiController {
@Autowired
private ElasticsearchRestTemplate elasticsearchRestTemplate;
/**
* 创建索引库
*
* @return
*/
@GetMapping("inner/createIndex")
public Result createIndex() {
//调用工具类创建索引
elasticsearchRestTemplate.createIndex(Goods.class);
//设置索引库映射信息
elasticsearchRestTemplate.putMapping(Goods.class);
return Result.ok();
}
/**
* 删除索引库
* @return
*/
@GetMapping("inner/deleteIndex")
public Result deleteIndex() {
elasticsearchRestTemplate.deleteIndex("goods");
return Result.ok();
}
}
在浏览器运行:
http://localhost:8203/api/list/inner/createIndex
通过kibana查看mapping
重点:attrs 数据类型必须是nested !
构建goods数据模型分析
Sku基本信息(详情业务已封装了接口)
Sku分类信息(详情业务已封装了接口)
Sku的品牌信息(无)
Sku对应的平台属性(详情业务已封装了接口)
YAPI接口地址:http://192.168.200.128:3000/project/11/interface/api/643
service-product
商品微服务模块中增加查询品牌RestFul接口实现
控制器ProductApiController
@Autowired
private BaseTrademarkService baseTrademarkService;
/**
* 根据品牌ID查询品牌信息
*
* @param tmId 品牌ID
* @return
*/
@GetMapping("/inner/getTrademark/{tmId}")
public BaseTrademark getTrademarkById(@PathVariable("tmId") Long tmId) {
BaseTrademark trademark = baseTrademarkService.getById(tmId);
return trademark;
}
在service-product-client
中ProductFeignClient提供Feign API接口
/**
* 根据品牌ID查询品牌信息
*
* @param tmId 品牌ID
* @return
*/
@GetMapping("/api/product/inner/getTrademark/{tmId}")
public BaseTrademark getTrademarkById(@PathVariable("tmId") Long tmId);
服务降级类
@Override
public BaseTrademark getTrademark(Long tmId) {
return null;
}
YAPI接口文档:
service-list
模块ListApiController 处理上下架请求
@Autowired
private SearchService searchService;
/**
* 测试接口,商品文档对象录入索引
* @param skuId
* @return
*/
@GetMapping("/inner/upperGoods/{skuId}")
public Result upperGoods(@PathVariable("skuId") Long skuId){
searchService.upperGoods(skuId);
return Result.ok();
}
/**
* 测试接口,商品文档删除
* @param skuId
* @return
*/
@GetMapping("/inner/lowerGoods/{skuId}")
public Result lowerGoods(@PathVariable("skuId") Long skuId){
searchService.lowerGoods(skuId);
return Result.ok();
}
SearchService
package com.atguigu.gmall.list.service;
public interface SearchService {
/**
* 构建商品索引库文档对象Goods,将文档存入索引库
* @param skuId
*/
void upperGoods(Long skuId);
/**
* 删除索引库文档对象
* @param skuId
*/
void lowerGoods(Long skuId);
}
SearchServiceImpl
package com.atguigu.gmall.list.service.impl;
import com.alibaba.fastjson.JSON;
import com.atguigu.gmall.list.model.Goods;
import com.atguigu.gmall.list.model.SearchAttr;
import com.atguigu.gmall.list.repository.GoodsRepository;
import com.atguigu.gmall.list.service.SearchService;
import com.atguigu.gmall.product.client.ProductFeignClient;
import com.atguigu.gmall.product.model.BaseAttrInfo;
import com.atguigu.gmall.product.model.BaseCategoryView;
import com.atguigu.gmall.product.model.BaseTrademark;
import com.atguigu.gmall.product.model.SkuInfo;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.xcontent.XContentType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;
/**
* @author: atguigu
* @create: 2023-04-26 14:17
*/
@Slf4j
@Service
@SuppressWarnings("all")
public class SearchServiceImpl implements SearchService {
@Autowired
private ProductFeignClient productFeignClient;
@Autowired
private GoodsRepository goodsRepository;
@Autowired
private RestHighLevelClient restHighLevelClient;
public static final String index_name = "goods";
@Autowired
private ThreadPoolExecutor threadPoolExecutor;
@Autowired
private RedisTemplate redisTemplate;
/**
* 将商品导入索引库
*
* @param skuId
*/
@Override
public void upperGoods(Long skuId) {
try {
//1.构建索引库文档对象Goods
Goods goods = new Goods();
goods.setId(skuId);
//2.远程调用商品微服务-根据商品SkuID查询商品基本信息
CompletableFuture<SkuInfo> skuInfoCompletableFuture = CompletableFuture.supplyAsync(() -> {
SkuInfo skuInfo = productFeignClient.getSkuInfo(skuId);
if (skuInfo != null) {
goods.setCategory3Id(skuInfo.getCategory3Id());
goods.setDefaultImg(skuInfo.getSkuDefaultImg());
goods.setTitle(skuInfo.getSkuName());
goods.setTmId(skuInfo.getTmId());
}
return skuInfo;
}, threadPoolExecutor);
BigDecimal skuPrice = productFeignClient.getSkuPrice(skuId);
if (skuPrice != null) {
goods.setPrice(skuPrice.doubleValue());
}
//3.远程调用商品微服务-根据分类ID查询分类信息.
CompletableFuture<Void> categoryCompletableFuture = skuInfoCompletableFuture.thenAcceptAsync(skuInfo -> {
BaseCategoryView categoryView = productFeignClient.getCategoryView(skuInfo.getCategory3Id());
if (categoryView != null) {
goods.setCategory1Id(categoryView.getCategory1Id());
goods.setCategory1Name(categoryView.getCategory1Name());
goods.setCategory2Id(categoryView.getCategory2Id());
goods.setCategory2Name(categoryView.getCategory2Name());
goods.setCategory3Id(categoryView.getCategory3Id());
goods.setCategory3Name(categoryView.getCategory3Name());
}
}, threadPoolExecutor);
//4.远程调用商品微服务-根据品牌ID查询品牌信息
CompletableFuture<Void> priceCompletableFuture = skuInfoCompletableFuture.thenAcceptAsync(skuInfo -> {
BaseTrademark trademark = productFeignClient.getTrademark(skuInfo.getTmId());
if (trademark != null) {
goods.setTmId(trademark.getId());
goods.setTmName(trademark.getTmName());
goods.setTmLogoUrl(trademark.getLogoUrl());
}
}, threadPoolExecutor);
//5.远程调用商品微服务-根据skuID查询平台属性列表
CompletableFuture<Void> attrInfoCompletableFuture = CompletableFuture.runAsync(() -> {
List<BaseAttrInfo> attrList = productFeignClient.getAttrList(skuId);
if (!CollectionUtils.isEmpty(attrList)) {
List<SearchAttr> searchAttrList = attrList.stream().map(baseAttrInfo -> {
SearchAttr searchAttr = new SearchAttr();
searchAttr.setAttrId(baseAttrInfo.getId());
searchAttr.setAttrName(baseAttrInfo.getAttrName());
searchAttr.setAttrValue(baseAttrInfo.getAttrValue());
return searchAttr;
}).collect(Collectors.toList());
goods.setAttrs(searchAttrList);
}
}, threadPoolExecutor);
goods.setCreateTime(new Date());
goods.setCreatedDate(new Date());
CompletableFuture.allOf(
skuInfoCompletableFuture,
priceCompletableFuture,
attrInfoCompletableFuture,
categoryCompletableFuture).join();
//6.调用JavaHighLevelRestClient完成文档新增 本质上发起http请求
//6.1 构建创建文档请求对象 IndexReqeust 封装新增数据对应索引库库名称 文档ID
IndexRequest request = new IndexRequest(index_name).id(skuId.toString());
//6.2 构建新增文档请求体参数 JSON
request.source(JSON.toJSONString(goods), XContentType.JSON);
//6.3 发起请求 请求ES
restHighLevelClient.index(request, RequestOptions.DEFAULT);
} catch (IOException e) {
log.error("[搜索服务]上架商品:{},失败原因:{}", skuId, e);
throw new RuntimeException(e);
}
}
/**
* 将指定商品从索引库删除
*
* @param skuId
*/
@Override
public void lowerGoods(Long skuId) {
try {
DeleteRequest request = new DeleteRequest(
index_name,
skuId.toString());
restHighLevelClient.delete(request, RequestOptions.DEFAULT);
} catch (IOException e) {
log.error("[搜索服务]下架商品:{},失败原因:{}", skuId, e);
throw new RuntimeException(e);
}
}
}
添加数据
通过kibana查看数据
说明:后期学习了MQ,我们可以根据后台系统添加和修改等操作,发送mq消息自动上下架商品
http://localhost:8203/api/list/inner/upperGoods/21
http://localhost:8203/api/list/inner/lowerGoods/21
搜索商品时,后面我们会根据热点排序,何时更新热点?我们在获取商品详情时调用更新
YAPI接口地址:http://192.168.200.128:3000/project/11/interface/api/739
ListApiController
package com.atguigu.gmall.list.api;
import com.atguigu.gmall.list.service.SearchService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author: atguigu
* @create: 2023-03-03 14:07
*/
@RestController
@RequestMapping("/api/list")
public class ListApiController {
@Autowired
private SearchService searchService;
/**
* 提供给详情服务调用:更新商品热度分值
* @param skuId
*/
@GetMapping("/inner/incrHotScore/{skuId}")
public void incrHotScore(@PathVariable("skuId") Long skuId){
searchService.incrHotScore(skuId);
}
}
SearchService
/**
* 提供给详情服务调用:更新商品热度分值
* @param skuId
*/
void incrHotScore(Long skuId);
SearchServiceImpl
@Autowired
private RedisTemplate redisTemplate;
/**
* 更新商品热度分值
* 1.更新Redis缓冲中商品分值
* 2.更新ES文档中商品分值(稀释写操作)
*
* @param skuId
*/
@Override
public void incrHotScore(Long skuId) {
try {
// 1.更新Redis缓冲中商品分值
String key = "hotScore";
Double skuHotScore = redisTemplate.opsForZSet().incrementScore(key, skuId.toString(), 1);
// 2.更新ES文档中商品分值(稀释写操作)
if (skuHotScore % 10 == 0) {
//2.1 创建更新文档请求对象 文档ID 文档分值
UpdateRequest updateRequest = new UpdateRequest(index_name, skuId.toString());
Goods goods = new Goods();
goods.setHotScore(skuHotScore.longValue());
updateRequest.doc(JSON.toJSONString(goods), XContentType.JSON);
restHighLevelClient.update(updateRequest, RequestOptions.DEFAULT);
}
} catch (IOException e) {
log.error("[搜索微服务]更新商品分值:{},异常信息:{}", skuId, e);
throw new RuntimeException(e);
}
}
在gmall-client
模块下搭建:service-list-client模块。搭建方式如service-item-client
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>gmall-client</artifactId>
<groupId>com.atguigu.gmall</groupId>
<version>1.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>service-list-client</artifactId>
</project>
提供远程调用的Feign接口
package com.atguigu.gmall.list.client;
import com.atguigu.gmall.list.client.impl.ListDegradeFeignClient;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@FeignClient(value = "service-list", fallback = ListDegradeFeignClient.class)
public interface ListFeignClient {
/**
* 提供给详情服务调用:更新商品热度分值
* @param skuId
*/
@GetMapping("/api/list/inner/incrHotScore/{skuId}")
public void incrHotScore(@PathVariable("skuId") Long skuId);
}
服务降级类
package com.atguigu.gmall.list.client.impl;
import com.atguigu.gmall.list.client.ListFeignClient;
import org.springframework.stereotype.Component;
/**
* @author: atguigu
* @create: 2023-03-03 14:36
*/
@Component
public class ListDegradeFeignClient implements ListFeignClient {
@Override
public void incrHotScore(Long skuId) {
}
}
在service-item
模块pom.xml中引入依赖
<dependency>
<groupId>com.atguigu.gmall</groupId>
<artifactId>service-list-client</artifactId>
<version>1.0</version>
</dependency>
接口调用,更新service-item
模块中ItemServiceImpl
汇总商品信息方法:getBySkuId
/**
* 查询商品信息,汇总详情页面需要数据
* 1.根据SkuID查询商品SKU基本信息包含图片列表-返回sku商品对象
* 2.根据三级分类ID查询所属分类信息
* 3.根据SkuID查询价格
* 4.根据spuId查询商品海报列表
* 5.根据skuId查询平台属性列表
* 6.根据spuID+SkuId查询销售属性 选中当前商品销售属性
* 7.根据spuID查询销售属性跟sku对照关系-选择销售属性组合,切换SKU
*
* @param skuId
* @return
*/
@Override
public Map<String, Object> getItemInfo(Long skuId) {
//...省略汇总商品七项数据代码
//8.TODO 远程调用搜索微服务更新商品热度
CompletableFuture<Void> goodsScoreCompletableFuture = CompletableFuture.runAsync(() -> {
listFeignClient.incrHotScore(skuId);
}, executor);
//x.等待所有异步任务并行执行完毕,主线程继续执行响应结果
CompletableFuture.allOf(
skuInfoCompletableFuture,
categorCompletableFuture,
priceCompletableFuture,
spuPosterListCompletableFuture,
skuAttrListCompletableFuture,
spuSaleAttrListCompletableFuture,
valuesSkuJsonCompletableFuture,
goodsScoreCompletableFuture
).join();
return mapResult;
}