👏作者简介:大家好,我是小童,Java开发工程师,CSDN博客博主,Java领域新星创作者
📕系列专栏:前端、Java、Java中间件大全、微信小程序、微信支付、若依框架、Spring全家桶
📧如果文章知识点有错误的地方,请指正!和大家一起学习,一起进步👀
🔥如果感觉博主的文章还不错的话,请👍三连支持👍一下博主哦
🍂博主正在努力完成2023计划中:以梦为马,扬帆起航,2023追梦人
亿级高并发电商项目-- 实战篇 --万达商城项目搭建 一 (商家端与用户端功能介绍、项目技术架构、数据库表结构等设计) | 亿级高并发电商项目-- 实战篇 --万达商城项目搭建 一 (商家端与用户端功能介绍、项目技术架构、数据库表结构等设计)_童小纯的博客-CSDN博客 |
亿级高并发电商项目-- 实战篇 --万达商城项目 二(Zookeeper、Docker、Dubbo-Admin等搭建工作 | 亿级高并发电商项目-- 实战篇 --万达商城项目 二(Zookeeper、Docker、Dubbo-Admin等搭建工作_童小纯的博客-CSDN博客 |
亿级高并发电商项目-- 实战篇 --万达商城项目 三(通用模块、商品服务模块、后台API模块、IDEA忽略文件显示等开发工作 | 亿级高并发电商项目-- 实战篇 --万达商城项目 三(通用模块、商品服务模块、后台API模块、IDEA忽略文件显示等开发工作_童小纯的博客-CSDN博客 |
亿级高并发电商项目-- 实战篇 --万达商城项目 四(Dashboard服务、设置统一返回格式与异常处理、Postman测试接口 ) | 亿级高并发电商项目-- 实战篇 --万达商城项目 四(Dashboard服务、设置统一返回格式与异常处理、Postman测试接口 )_童小纯的博客-CSDN博客 |
亿级高并发电商项目-- 实战篇 --万达商城项目 五 (用户服务模块、管理员模块功能 增、删、改、查 、分页,前端工程) | 亿级高并发电商项目-- 实战篇 --万达商城项目 五 (用户服务模块、管理员模块功能 增、删、改、查 、分页,前端工程)_童小纯的博客-CSDN博客 |
亿级高并发电商项目-- 实战篇 --万达商城项目 六(编写角色管理、用户权限(Spring Security认证授权)、管理员管理等模块) | 亿级高并发电商项目-- 实战篇 --万达商城项目 六(编写角色管理、用户权限(Spring Security认证授权)、管理员管理等模块)_童小纯的博客-CSDN博客 |
亿级高并发电商项目-- 实战篇 --万达商城项目 七(品牌模块、商品类型模块等开发) | 亿级高并发电商项目-- 实战篇 --万达商城项目 七(品牌模块、商品类型模块等开发)_童小纯的博客-CSDN博客 |
亿级高并发电商项目-- 实战篇 --万达商城项目 八(安装FastDFS、安装Nginx、文件服务模块、文件上传功能、商品功能与秒杀商品等功能) | 亿级高并发电商项目-- 实战篇 --万达商城项目 八(安装FastDFS、安装Nginx、文件服务模块、文件上传功能、商品功能与秒杀商品等功能)_童小纯的博客-CSDN博客 |
亿级高并发电商项目-- 实战篇 --万达商城项目 九(广告服务、安装Redis优化用户缓存、广告服务实现类等开发) | 亿级高并发电商项目-- 实战篇 --万达商城项目 九(广告服务、安装Redis优化用户缓存、广告服务实现类等开发)_童小纯的博客-CSDN博客 |
亿级高并发电商项目-- 实战篇 --万达商城项目 十(安装与配置Elasticsearch和kibana、编写搜索功能、向ES同步数据库商品数据) | 亿级高并发电商项目-- 实战篇 --万达商城项目 十(安装与配置Elasticsearch和kibana、编写搜索功能、向ES同步数据库商品数据)_童小纯的博客-CSDN博客 |
编写补齐关键字功能
电商网站的搜索框都会提供根据前缀补齐关键字的功能,接下来我们编写这一功能。
1、编写搜索服务接口实现类
// 自动补齐
@Override
public List<String> autoSuggest(String
keyword) {
// 1.创建补全条件
SuggestBuilder suggestBuilder = new SuggestBuilder();
SuggestionBuilder suggestionBuilder = SuggestBuilders
.completionSuggestion("tags")
.prefix(keyword)
.skipDuplicates(true)
.size(10);
suggestBuilder.addSuggestion("prefix_suggestion", suggestionBuilder);
// 2.发送请求
SearchResponse response = template.suggest(suggestBuilder,IndexCoordinates.of("goods"));
// 3.处理结果
List<String> result = response
.getSuggest()
.getSuggestion("prefix_suggestion")
.getEntries()
.get(0)
.getOptions()
.stream()
.map(Suggest.Suggestion.Entry.Option::getText)
.map(Text::toString)
.collect(Collectors.toList());
return result;
}
2、编写搜索控制器
/**
* 用户商品搜索
*/
@RestController
@RequestMapping("/goodsSearch")
public class GoodsSearchController {
@DubboReference
private GoodsESService goodsESService;
/**
* 自动补齐关键字
* @param keyword 被补齐的词
* @return 补齐的关联词集合
*/
@GetMapping("/autoSuggest")
public BaseResult<List<String>> autoSuggest(String keyword){
List<String> keywords = goodsESService.autoSuggest(keyword);
return BaseResult.ok(keywords);
}
}
3、测试接口
编写商品搜索功能
搜索功能的编写比较复杂,首先搜索条件繁多,有关键字、价格、 品牌、规格等。其次返回的结果除了搜索到的商品,还要返回搜索面板,包含关键字对应的品牌、品类、规格等,还要将搜索条件回 显回去,供前端操作。接下来我们编写商品的搜索功能。
1、重写搜索产品功能
// 搜索产品
@Override
public GoodsSearchResult search(GoodsSearchParam goodsSearchParam)
{
// 1.构造ES搜索条件
// 2.搜索
// 3.将查询结果封装为Page对象
// 4.封装结果对象
// 4.1 查询结果
// 4.2 查询查询参数
// 4.3 查询面板
return null;
}
2、编写构造ES搜索条件功能
/**
* 构造搜索条件
* @param goodsSearchParam 查询条件对象
* @return 搜索条件对象
*/
public NativeSearchQuery buildQuery(GoodsSearchParam goodsSearchParam){
// 1.创建复杂查询条件对象
BoolQueryBuilder builder = QueryBuilders.boolQuery();
// 2.如果查询条件有关键词,关键词可以匹配商品名、副标题、品牌字段;否则查询所有商品
if(!StringUtils.hasText(goodsSearchParam.getKeyword())){
MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();
builder.must(matchAllQueryBuilder);
}else {
String keyword = goodsSearchParam.getKeyword();
MultiMatchQueryBuilder multiMatchQueryBuilder = QueryBuilders.multiMatchQuery(keyword,"goodsName", "caption", "brand");
builder.must(multiMatchQueryBuilder);
}
// 3.如果查询条件有品牌,则精准匹配品牌
String brand = goodsSearchParam.getBrand();
if (StringUtils.hasText(brand)){
TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("brand", brand);
builder.must(termQueryBuilder);
}
// 4.如果查询条件有价格,则匹配价格
Double highPrice = goodsSearchParam.getHighPrice();
Double lowPrice = goodsSearchParam.getLowPrice();
if (highPrice != null && highPrice != 0){
RangeQueryBuilder lte = QueryBuilders.rangeQuery("price").lte(highPrice);
builder.must(lte);
}
if (lowPrice != null && lowPrice != 0)
{
RangeQueryBuilder gte = QueryBuilders.rangeQuery("price").gte(lowPrice);
builder.must(gte);
}
// 5.如果查询条件有规格项,则精准匹配规格项
Map<String, String> specificationOptions = goodsSearchParam.getSpecificationOption();
if (specificationOptions != null && specificationOptions.size() > 0){
Set<Map.Entry<String, String>> entries = specificationOptions.entrySet();
for (Map.Entry<String, String> entry : entries) {
String key = entry.getKey();
String value = entry.getValue();
if (StringUtils.hasText(key)){
TermQueryBuilder termQuery = QueryBuilders.termQuery("specification." + key + ".keyword", value);
builder.must(termQuery);
}
}
}
// 6.添加分页条件
PageRequest pageable = PageRequest.of(goodsSearchParam.getPage() - 1, goodsSearchParam.getSize());
// 查询构造器,添加条件和分页
NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
nativeSearchQueryBuilder.withQuery(builder).withPageable(pageable);
// 7.如果查询条件有排序,则添加排序条件
String sortFiled = goodsSearchParam.getSortFiled();
String sort = goodsSearchParam.getSort();
SortBuilder sortBuilder = null;
if (StringUtils.hasText(sort) && StringUtils.hasText(sortFiled)){
// 新品的正序是id的倒序
if (sortFiled.equals("NEW")){
sortBuilder = SortBuilders.fieldSort("id");
if (sort.equals("ASC")){
sortBuilder.order(SortOrder.DESC);
}
if (sort.equals("DESC")){
sortBuilder.order(SortOrder.ASC);
}
}
if (sortFiled.equals("PRICE")){
sortBuilder = SortBuilders.fieldSort("price");
if (sort.equals("ASC")){
sortBuilder.order(SortOrder.ASC);
}
if (sort.equals("DESC")){
sortBuilder.order(SortOrder.DESC);
}
}
nativeSearchQueryBuilder.withSorts(sortBuilder);
}
// 8.返回查询条件对象
NativeSearchQuery query = nativeSearchQueryBuilder.build();
return query;
}
3、重写搜索产品功能
@Override
public GoodsSearchResult
search(GoodsSearchParam goodsSearchParam)
{
// 1.构造ES搜索条件
NativeSearchQuery query = buildQuery(goodsSearchParam);
// 2.搜索
SearchHits<GoodsES> search = template.search(query, GoodsES.class);
// 3.将查询结果封装为Page对象
// 3.1 将SearchHits转为List
List<GoodsES> content = new ArrayList();
for (SearchHit<GoodsES> goodsESSearchHit : search) {
GoodsES goodsES = goodsESSearchHit.getContent();
content.add(goodsES);
}
// 3.2 将List转为MP的Page对象
Page<GoodsES> page = new Page();
page.setCurrent(goodsSearchParam.getPage()) // 当前页
.setSize(goodsSearchParam.getSize()) // 每页条数
.setTotal(search.getTotalHits())// 总条数
.setRecords(content); // 结果集
// 4.封装结果对象
// 4.1 查询结果
GoodsSearchResult result = new GoodsSearchResult();
result.setGoodsPage(page);
// 4.2 查询参数
result.setGoodsSearchParam(goodsSearchParam);
// 4.3 查询面板
buildSearchPanel(goodsSearchParam,result);
return result;
}
4 编写封装查询面板功能,查询面板中包含品牌、分类、规格项。 根据查询条件,查询出20条最符合的数据,遍历这些数据,找到它们的品牌、分类、规格项,最后封装到查询结果中。写法如 下:
/**
* 封装查询面板,即根据查询条件,找到查询结果关联度前20名的商品进行封装
* @param goodsSearchParam
* @param goodsSearchResult
*/
public void buildSearchPanel(GoodsSearchParam goodsSearchParam,GoodsSearchResult goodsSearchResult){
// 1.构造搜索条件
goodsSearchParam.setPage(1);
goodsSearchParam.setSize(20);
goodsSearchParam.setSort(null);
goodsSearchParam.setSortFiled(null);
NativeSearchQuery query = buildQuery(goodsSearchParam);
// 2.搜索
SearchHits<GoodsES> search = template.search(query, GoodsES.class);
// 3.将结果封装为List对象
List<GoodsES> content = new ArrayList();
for (SearchHit<GoodsES> goodsESSearchHit : search) {
GoodsES goodsES = goodsESSearchHit.getContent();
content.add(goodsES);
}
// 4.遍历集合,封装查询面板
// 商品相关的品牌列表
Set<String> brands = new HashSet();
// 商品相关的类型列表
Set<String> productTypes = new HashSet();
// 商品相关的规格列表
Map<String, Set<String>> specifications = new HashMap();
for (GoodsES goodsES : content) {
// 获取品牌
brands.add(goodsES.getBrand());
// 获取类型
List<String> productType = goodsES.getProductType();
productTypes.addAll(productType);
// 获取规格
Map<String, List<String>> specification = goodsES.getSpecification();
Set<Map.Entry<String, List<String>>> entries = specification.entrySet();
for (Map.Entry<String, List<String>> entry : entries) {
// 规格名
String key = entry.getKey();
// 规格值
List<String> value = entry.getValue();
// 如果没有遍历出该规格,新增键值对,如果已经遍历出该规格,则向规格中添加规格项
if(!specifications.containsKey(key)){
specifications.put(key,new HashSet(value));
}else{
specifications.get(key).addAll(value);
}
}
}
goodsSearchResult.setBrands(brands);
goodsSearchResult.setProductType(productTypes);
goodsSearchResult.setSpecifications(specifications);
}
5、编写搜索控制器
/**
* 搜索商品
* @param goodsSearchParam 搜索条件
* @return 搜索结果
*/
@PostMapping("/search")
public BaseResult<GoodsSearchResult> search(@RequestBody GoodsSearchParam
goodsSearchParam){
GoodsSearchResult result = searchService.search(goodsSearchParam);
return BaseResult.ok(result);
}
6、测试接口
编写根据id查询商品详情功能
用户查询到商品列表后,再查询商品详情需要从数据库查询,因为 ES中的数据不全。所以我们需要在商品服务中添加根据id查询商品详情功能:
1、在 GoodsMapper 中添加根据id查询商品详情方法
// 根据id查询商品详情
GoodsDesc findDesc(Long id);
2、编写映射文件 GoodsMapper.xml
<select id="findDesc"
resultMap="goodsDescMapper">
SELECT
bz_goods.id bid,
bz_goods.goodsName goodsName,
bz_goods.caption caption,
bz_goods.price price,
bz_goods.headerPic headerPic,
bz_goods.introduction introduction,
bz_goods.isMarketable isMarketable,
bz_goods.brandId brandId,
bz_brand.`name` brandName,
type1.id type1Id,
type1.`name` type1Name,
type1.`level` type1Level,
type1.parentId type1ParentId,
type2.id type2Id,
type2.`name` type2Name,
type2.`level` type2Level,
type2.parentId type2ParentId,
type3.id type3Id,
type3.`name` type3Name,
type3.`level` type3Level,
type3.parentId type3ParentId,
bz_goods_image.id imageId,
bz_goods_image.imageTitle imageTitle,
bz_goods_image.imageUrl imageUrl,
bz_specification.id specificationId,
bz_specification.specName specName,
bz_specification.productTypeId
productTypeId,
bz_specification_option.id optionId,
bz_specification_option.optionName
optionName
FROM
bz_goods,
bz_goods_image,
bz_brand,
bz_specification,
bz_specification_option,
bz_goods_specification_option,
bz_product_type AS type1,
bz_product_type AS type2,
bz_product_type AS type3
WHERE bz_goods.id =
bz_goods_specification_option.gid
AND
bz_goods_specification_option.optionId =
bz_specification_option.id
AND bz_specification.id =
bz_specification_option.specId
AND bz_goods.brandId = bz_brand.id
AND bz_goods.id =
bz_goods_image.goodsId
AND bz_goods.productType1Id = type1.id
AND bz_goods.productType2Id = type2.id
AND bz_goods.productType3Id = type3.id
AND bz_goods.id = #{id}
</select>
3、编写商品服务接口和商品服务接口实现类
public interface GoodsService {
// 查询商品详情
GoodsDesc findDesc(Long id);
}
public class GoodsServiceImpl implements GoodsService {
@Override
public GoodsDesc findDesc(Long id) {
return goodsMapper.findDesc(id);
}
}
4、编写搜索控制器
/**
* 用户商品搜索
*/
@RestController
@RequestMapping("/user/goodsSearch")
public class GoodsSearchController {
@DubboReference
private GoodsESService goodsESService;
@DubboReference
private GoodsService goodsService;
/**
* 根据id查询商品详情
* @param id 商品id
* @return 商品详情
*/
@GetMapping("/findDesc")
public BaseResult<GoodsDesc> findDesc(Long id) {
GoodsDesc goodsDesc = goodsService.findDesc(id);
return BaseResult.ok(goodsDesc);
}
}
5、测试接口
管理员操作商品后同步到ES中
管理员在数据库增删改商品后,需要将商品数据同步到ES中,这样用户才能在第一时间搜索到最新数据。写法如下:
编写删除ES商品方法
在搜索接口实现类编写删除ES商品方法,商品下架后调用该方法
// 删除ES中的商品
@Override
public void delete(Long id) {
goodsESRepository.deleteById(id);
}
修改商品服务接口实现类
@DubboService
public class GoodsServiceImpl implements
GoodsService {
@Autowired
private GoodsMapper goodsMapper;
@Autowired
private GoodsImageMapper goodsImageMapper;
@DubboReference
private SearchService searchService;
@Override
public void add(Goods goods) {
// 插入商品数据
goodsMapper.insert(goods);
// 插入图片数据
Long goodsId = goods.getId(); // 获取商品主键
List<GoodsImage> images = goods.getImages(); // 商品图片
for (GoodsImage image : images) {
image.setGoodsId(goodsId); // 给图片设置商品id
goodsImageMapper.insert(image); //插入图片
}
// 插入商品_规格项数据
// 1.获取规格
List<Specification> specifications = goods.getSpecifications();
// 2.获取规格项
List<SpecificationOption> options = new ArrayList(); //规格项集合
// 遍历规格,获取规格中的所有规格项
for (Specification specification : specifications) {
options.addAll(specification.getSpecificationOptions());
}
// 3.遍历规格项,插入商品_规格项数据
for (SpecificationOption option : options) {
goodsMapper.addGoodsSpecificationOption(goodsId,option.getId());
}
// 将商品数据同步到es中
GoodsDesc goodsDesc = findDesc(goodsId);
searchService.syncGoodsToES(goodsDesc);
}
@Override
public void update(Goods goods) {
// 删除旧图片数据
Long goodsId = goods.getId(); // 商品id
QueryWrapper<GoodsImage> queryWrapper = new QueryWrapper();
queryWrapper.eq("goodsId",goodsId);
goodsImageMapper.delete(queryWrapper);
// 删除旧规格项数据
goodsMapper.deleteGoodsSpecificationOption(goodsId);
// 插入商品数据
goodsMapper.updateById(goods);
// 插入图片数据
List<GoodsImage> images = goods.getImages(); // 商品图片
for (GoodsImage image : images) {
image.setGoodsId(goodsId); // 给图片设置商品id
goodsImageMapper.insert(image); //插入图片
}
// 插入商品_规格项数据
// 1.获取规格
List<Specification> specifications = goods.getSpecifications();
// 2.获取规格项
List<SpecificationOption> options = new ArrayList(); //规格项集合
// 遍历规格,获取规格中的所有规格项
for (Specification specification : specifications) {
options.addAll(specification.getSpecificationOptions());
}
// 3.遍历规格项,插入商品_规格项数据
for (SpecificationOption option : options) {
goodsMapper.addGoodsSpecificationOption(goodsId,option.getId());
}
// 将商品数据同步到es中
GoodsDesc goodsDesc = findDesc(goodsId);
searchService.syncGoodsToES(goodsDesc);
}
@Override
public void putAway(Long id, Boolean isMarketable) {
goodsMapper.putAway(id,isMarketable);
// 上架时数据同步到ES,下架时删除ES数据
if (isMarketable){
GoodsDesc goodsDesc = findDesc(id);
searchService.syncGoodsToES(goodsDesc);
}else {
searchService.delete(id);
}
}
}
在商品同步到ES的功能中,商品服务使用Dubbo调用了搜索服务,这种方式有以下两个问题:
1、服务间耦合严重,即商品服务的启动必须依赖搜索服务。
2 、运行效率低,在管理员增删改商品后调用搜索服务会等待同步结果,比较浪费时间。
此时我们可以使用消息队列解决这一问题。即管理员增删改商 品后,向消息队列发送消息,搜索服务监听消息,搜索服务拿 到消息后同步数据即可。这样既能优化管理员体验,也可以减少服务间的依赖。
安装Erlang
RabbitMQ是使用Erlang语言编写的,所以在安装RabbitMQ前需要先安装Erlang环境
1、安装Erlang所需的依赖
yum install -y epel-release
2、添加存储库条目
get https://packages.erlangsolutions.com/erlang-solutions-1.0-1.noarch.rpm
rpm -Uvh erlang-solutions-1.0-1.noarch.rpm
3、安装Erlang
yum install erlang-24.2.1
4、查看Erlang是否安装成功
erl -version
安装RabbitMQ
1、RabbitMQ是通过主机名进行访问的,必须给虚拟机添加主机名
# 修改文件
vim /etc/sysconfig/network
# 添加如下内容
NETWORKING=yes
HOSTNAME=itbaizhan
# 修改文件
vim /etc/hosts
# 添加如下内容
服务器ip itbaizhan
2、使用rz命令上传RabbitMQ压缩文件
3、安装RabbitMQ
# 解压RabbitMQ
tar xf rabbitmq-server-generic-unix-3.9.13.tar.xz
# 重命名:
mv rabbitmq_server-3.9.13 rabbitmq
# 移动文件夹:
mv rabbitmq /usr/local/
4、配置环境变量
# 编辑/etc/profile文件
vim /etc/profile
#添加如下内容
export PATH=$PATH:/usr/local/rabbitmq/sbin
# 运行文件,让修改内容生效
source /etc/profile
5、配置允许使用guest远程访问
# 创建配置文件夹
mkdir -p /usr/local/rabbitmq/etc/rabbitmq
# 创建配置文件
vim /usr/local/rabbitmq/etc/rabbitmq/rabbitmq.conf
# 添加如下内容
loopback_users=none
6、开启管控台插件
rabbitmq-plugins enable
rabbitmq_management
7、后台运行
#启动rabbitmq
rabbitmq-server -detached
#停止rabbitmq
rabbitmqctl stop
8、通过管控台访问RabbitMQ
路径: http://ip地址:15672 ,用户名: guest ,密码: guest
修改商品服务
1、添加RabbitMQ起步依赖
<!-- rabbitmq -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
2、添加RabbitMQ相关配置
spring:
# 数据源
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql:///baizhanshopping?serverTimezone=UTC
username: root
password01: 123456
# rabbitmq
rabbitmq:
host: 192.168.100.131
port: 5672
username: guest
password: guest
virtual-host: /
3、编写配置文件,创建交换机和队列
@Configuration
public class RabbitConfig {
// 交换机
private final String GOODS_EXCHANGE = "goods_exchange";
// 同步商品数据队列
private final String SYNC_GOODS_QUEUE = "sync_goods_queue";
// 删除商品数据队列
private final String DEL_GOODS_QUEUE = "del_goods_queue";
// 创建交换机
@Bean(GOODS_EXCHANGE)
public Exchange getExchange() {
return ExchangeBuilder
.topicExchange(GOODS_EXCHANGE) // 交换机类型
.durable(true) // 是否持久化
.build();
}
// 创建队列
@Bean(SYNC_GOODS_QUEUE)
public Queue getQueue1() {
return new Queue(SYNC_GOODS_QUEUE); // 队列名
}
@Bean(DEL_GOODS_QUEUE)
public Queue getQueue2() {
return new Queue(DEL_GOODS_QUEUE); // 队列名
}
// 交换机绑定队列
@Bean
public Binding bindQueue1(@Qualifier(GOODS_EXCHANGE) Exchange exchange,
@Qualifier(SYNC_GOODS_QUEUE) Queue queue)
{
return BindingBuilder
.bind(queue)
.to(exchange)
.with("#.sync_goods.#")
.noargs();
}
@Bean
public Binding bindQueue2(@Qualifier(GOODS_EXCHANGE) Exchange exchange,
@Qualifier(DEL_GOODS_QUEUE) Queue queue)
{
return BindingBuilder
.bind(queue)
.to(exchange)
.with("#.del_goods.#")
.noargs();
}
}
4、修改商品服务实现类,增删改商品后发送消息
@DubboService
public class GoodsServiceImpl implements
GoodsService {
@Autowired
private GoodsMapper goodsMapper;
@Autowired
private GoodsImageMapper goodsImageMapper;
@Autowired
private RabbitTemplate rabbitTemplate;
@Override
public void add(Goods goods) {
// 插入商品数据
goodsMapper.insert(goods);
// 插入图片数据
Long goodsId = goods.getId(); // 获取商品主键
List<GoodsImage> images = goods.getImages(); // 商品图片
for (GoodsImage image : images) {
image.setGoodsId(goodsId); //给图片设置商品id
goodsImageMapper.insert(image); // 插入图片
}
// 插入商品_规格项数据
List<Specification> specifications = goods.getSpecifications(); // 获取规格
List<SpecificationOption> options = new ArrayList(); // 规格项集合
// 遍历规格,获取规格中的所有规格项
for (Specification specification : specifications) {
options.addAll(specification.getSpecificationOptions());
}
// 遍历规格项,插入商品_规格项数据
for (SpecificationOption option : options) {
goodsMapper.addGoodsSpecificationOption(goodsId,option.getId());
}
// 将商品数据同步到ES中
rabbitTemplate.convertAndSend("goods_exchange","sync_goods",findById(goodsId));
}
@Override
public void update(Goods goods) {
// 删除旧图片数据
Long goodsId = goods.getId(); //商品id
QueryWrapper<GoodsImage> queryWrapper = new QueryWrapper();
queryWrapper.eq("goodsId",goodsId);
goodsImageMapper.delete(queryWrapper);
// 删除旧规格项数据
goodsMapper.deleteGoodsSpecificationOption(goodsId);
// 插入商品数据
goodsMapper.updateById(goods);
// 插入图片数据
List<GoodsImage> images = goods.getImages(); // 商品图片
for (GoodsImage image : images) {
image.setGoodsId(goodsId); //给图片设置商品id
goodsImageMapper.insert(image); // 插入图片
}
// 插入商品_规格项数据
List<Specification> specifications = goods.getSpecifications(); // 获取规格
List<SpecificationOption> options = new ArrayList(); // 规格项集合
// 遍历规格,获取规格中的所有规格项
for (Specification specification : specifications) {
options.addAll(specification.getSpecificationOptions());
}
// 遍历规格项,插入商品_规格项数据
for (SpecificationOption option : options) {
goodsMapper.addGoodsSpecificationOption(goodsId,option.getId());
}
// 将商品数据同步到ES中
rabbitTemplate.convertAndSend("goods_exchange","sync_goods",findById(goodsId));
}
@Override
public GoodsDesc findById(Long id) {
return goodsMapper.findById(id);
}
@Override
public List<GoodsDesc> findAll() {
return goodsMapper.findAll();
}
@Override
public void putAway(Long id, Boolean isMarketable) {
goodsMapper.putAway(id,isMarketable);
if (isMarketable){
// 上架时同步到ES
rabbitTemplate.convertAndSend("goods_exchange","sync_goods",findById(id));
}else{
// 下架删除ES数据
rabbitTemplate.convertAndSend("goods_exchange","del_goods",id);
}
}
}
修改搜索服务
1、添加RabbitMQ起步依赖
<!-- rabbitmq -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
2 、添加RabbitMQ相关配置
spring:
# 数据源
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql:///baizhanshopping?serverTimezone=UTC
username: root
password01: 123456
# elasticsearch
elasticsearch:
uris: http://192.168.100.131:9200
# rabbitmq
rabbitmq:
host: 192.168.100.131
port: 5672
username: guest
password: guest
virtual-host: /
3、修改搜索服务实现类,监听队列,处理消息
@DubboService
@Service
public class GoodsESServiceImpl implements GoodsESService {
// 监听同步商品队列
@RabbitListener(queues = "sync_goods_queue")
public void listenSyncQueue(GoodsDesc goodsDesc){
syncGoodsToES(goodsDesc);
}
// 监听删除商品队列
@RabbitListener(queues = "del_goods_queue")
public void listenDelQueue(Long id){
delete(id);
}
}