亿级高并发电商项目-- 实战篇 --万达商城项目 十一(编写商品搜索功能、操作商品同步到ES、安装RabbitMQ与Erlang,配置监听队列与消息队列)

news2024/11/17 4:43:22

 

 

👏作者简介:大家好,我是小童,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);
   }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/354713.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Sandboxie-沙箱软件-Plus版本(Qt)-主框架程序-SandMan.exe-创建语言文件-tr-Qt-语言国际化

文章目录1.功能介绍2.Qt语言国际化3.设置软件的语言版本4.作者答疑1.功能介绍 沙箱软件的增强版本采用Qt架构开发&#xff0c;核心模块与经典版本相同&#xff0c;本文主要介绍SandMan.exe这个主程序代码。在main.cpp这个入口函数里&#xff0c;有主窗口入口&#xff0c;如下所…

2.5|iot冯|方元-嵌入式linux系统开发入门|2.13+2.18

一、 Linux 指令操作题&#xff08;共5题&#xff08;共 20 分&#xff0c;每小题 4分&#xff09;与系统工作、系统状态、工作目录、文件、目录、打包压缩与搜索等主题相关。1.文件1.1文件属性1.2文件类型属性字段的第1个字符表示文件类型&#xff0c;后9个字符中&#xff0c;…

【物联网】智慧农业病虫害精准辨识竞赛思路及代码分享

来源&#xff1a;投稿 作者&#xff1a;LSC 编辑&#xff1a;学姐 比赛官网: https://www.dataglobal.cn/cmpt/signUpInfo200.html 任务描述 请参赛者设计智慧农业病虫害检测系统&#xff0c;给出一体化问题解决方案&#xff0c;鼓励参赛选手结合某一果园/农作物实际情况建立…

使用 URLSearchParams 解析和管理URL query参数

介绍 首先 URLSearchParams是一个构造函数&#xff0c;会生成一个URLSearchParams对象&#xff0c;参数类型&#xff1a; 不传 &#xff5c; string &#xff5c; object &#xff5c; URLSearchParams&#xff0c; 并且遇到特殊字符它会自动帮我们encode 和 decode const ur…

Java模块化概述

3 模块化 3.1 模块化概述 Java语言随着这些年的发展已经成为了一]影响深远的编程语言&#xff0c;无数平台,系统都采用Java语言编写。但是&#xff0c;伴随着发展&#xff0c;Java也越来越庞大&#xff0c;逐渐发展成为-门“臃肿” 的语言。而且&#xff0c;无论是运行个大型的…

Vulnhub 渗透练习(五)—— lazysysadmin1

环境搭建 下载链接 vmware 打开靶机&#xff0c;nat 网络适配&#xff0c;攻击机同样。 信息收集 一个一个的看过去&#xff0c;这边就不贴图了。 漏洞挖掘 用 kail 的 wpscan 扫一下 wordpress&#xff0c;没发现漏洞。 ┌──(geng㉿geng)-[~] └─$ wpscan --url http…

【Linux06-基础IO】4.5万字的基础IO讲解

前言 本期分享基础IO的知识&#xff0c;主要有&#xff1a; 复习C语言文件操作文件相关的系统调用文件描述符fd理解Linux下一切皆文件缓冲区文件系统软硬链接动静态库的理解和制作动静态编译 博主水平有限&#xff0c;不足之处望请斧正&#xff01; C语言文件操作 #再谈文件…

SQLSERVER2019安装步骤过程

第一步官网下载SQLSERVER软件包 目前官网只能下载最新版本2022版本。 通过迅雷下载网址 SQL Server 2019 Enterprise (x64) - DVD (Chinese-Simplified)企业版 ed2k://|file|cn_sql_server_2019_enterprise_x64_dvd_2bfe815a.iso|1632086016|58C258FF0F1D006DD3C1F5F17AF3E…

ELK_Elasticsearch环境搭建

目录 一、Windows安装elasticsearch 1、安装JDK 2、下载和解压 3、配置文件 4、启动 5、检查ES是否启动成功 6、浏览器访问 二、 Windows安装Kibana 一、Windows安装elasticsearch 1、安装JDK 安装JDK&#xff0c;至少1.8.0_73以上版本&#xff0c;验证&#xff1a;j…

dbForge Source Control for SQL Server 2.5.X Crack

SQL Server功能概述 的 dbForge 源代码管理 dbForge Source Control for SQL Server 是一个可视化的 SSMS 插件&#xff0c;具有简单易用的界面&#xff0c;可帮助您轻松跟踪 SQL Server 数据库对象中的更改内容、更改时间和原因。该工具使您能够将数据库连接到多个版本控制系统…

【Python学习笔记】40.Python3 SMTP发送邮件

前言 SMTP&#xff08;Simple Mail Transfer Protocol&#xff09;即简单邮件传输协议,它是一组用于由源地址到目的地址传送邮件的规则&#xff0c;由它来控制信件的中转方式。 Python3 SMTP发送邮件 python的smtplib提供了一种很方便的途径发送电子邮件。它对smtp协议进行了…

CSS基础选择器,你认识多少?

前言在上一文初识CSS中&#xff0c;我们了解到了其格式&#xff1a;选择器{ }在初步尝试使用时&#xff0c;我们笼统的直接输入了p { }以选择p标签来对其操作&#xff0c;而这一章节里&#xff0c;我们再进一步探索有关基础选择器的相关内容&#xff0c;理解选择器的作用。选择…

2019蓝桥杯真题平方序列(填空题) C语言/C++

题目描述 本题为填空题&#xff0c;只需要算出结果后&#xff0c;在代码中使用输出语句将所填结果输出即可。 小明想找到两个正整数 X 和 Y&#xff0c;满足2019<X<Y;2019^2, X^2, Y^2组成等差数列。 请你求出在所有可能的解中&#xff0c;XY 的最小值是多少&#xff1f…

QT(17)- QNetworkAccessManager

QT&#xff08;17&#xff09;- QNetworkAccessManager1 简介2 公有类型3 函数3.1 addStrictTransportSecurityHosts3.2 autoDeleteReplies3.3 cache3.4 clearAccessCache3.5 clearConnectionCache3.6 connectToHost3.7 connectToHostEncrypted3.8 cookieJar3.9 deleteResource…

Metasploit框架基础(一)

文章目录前言一、基础认知二、批量POC/EXP的构想三、poc检测框架的简单实现四、xray五、Meatsploit框架参考前言 Metasploit 一款渗透测试框架漏洞利用的集合与构建和定制满足你的需求的基础漏洞利用和验证的工具 这几个说法都是百度或者官方文档中出现的手法&#xff0c;说…

ASEMI代理FGH60N60SFD,安森美FGH60N60SFD原装IGBT

编辑-Z 安森美FGH60N60SFD原装IGBT参数&#xff1a; 型号&#xff1a;FGH60N60SFD 集电极到发射极电压&#xff08;VCES&#xff09;&#xff1a;600V 栅极到发射极电压&#xff08;VGES&#xff09;&#xff1a;20V 收集器电流&#xff08;IC&#xff09;&#xff1a;120…

【Spark分布式内存计算框架——Spark SQL】9. Dataset(下)RDD、DF与DS转换与面试题

5.3 RDD、DF与DS转换 实际项目开发中&#xff0c;常常需要对RDD、DataFrame及Dataset之间相互转换&#xff0c;其中要点就是Schema约束结构信息。 1&#xff09;、RDD转换DataFrame或者Dataset 转换DataFrame时&#xff0c;定义Schema信息&#xff0c;两种方式转换为Dataset时…

由浅入深掌握 Python 进程间通信的各类方式

由浅入深掌握 Python 多进程间通信各类方式1、为什么要掌握进程间通信2、进程间各类通信方式简介3、消息机制通信1) 管道 Pipe 通信方式2) 消息队列Queue 通信方式4、同步机制通信(1) 进程间同步锁 – Lock(2) 子进程间协调机制 -- Event5、共享内存方式通信(1) 共享变量(2) 共…

【Bluetooth开发】蓝牙开发入门

BLE 蓝牙设备在生活中无处不在&#xff0c;但是我们也只是将其作为蓝牙模块进行使用&#xff0c;发送简单的AT命令实现数据收发。 那么&#xff0c;像对于一些复杂的使用场合&#xff1a;“车载蓝牙”、"智能手表"、“蓝牙音箱”等&#xff0c;我们不得不去了解底层…

千锋教育+计算机四级网络-计算机网络学习-04

UDP概述 UDP协议 面向无连接的用户数据报协议&#xff0c;在传输数据前不需要先建立连接&#xff1b;目地主机的运输层收到UDP报文后&#xff0c;不需要给出任何确认 UDP特点 相比TCP速度稍快些简单的请求/应答应用程序可以使用UDP对于海量数据传输不应该使用UDP广播和多播应用…