亿级高并发电商项目-- 实战篇 --万达商城项目 十一(编写商品搜索功能、操作商品同步到ES、安装RabbitMQ与Erlang,配置监听队列与消息队列)
👏作者简介:大家好,我是小童,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 {@DubboReferenceprivate 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转为ListList<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">SELECTbz_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
optionNameFROMbz_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 type3WHERE bz_goods.id =
bz_goods_specification_option.gidAND
bz_goods_specification_option.optionId =
bz_specification_option.idAND bz_specification.id =
bz_specification_option.specIdAND bz_goods.brandId = bz_brand.idAND bz_goods.id =
bz_goods_image.goodsIdAND bz_goods.productType1Id = type1.idAND bz_goods.productType2Id = type2.idAND bz_goods.productType3Id = type3.idAND bz_goods.id = #{id}
</select>
3、编写商品服务接口和商品服务接口实现类
public interface GoodsService {// 查询商品详情GoodsDesc findDesc(Long id);
}
public class GoodsServiceImpl implements GoodsService {@Overridepublic GoodsDesc findDesc(Long id) {return goodsMapper.findDesc(id);}
}
4、编写搜索控制器
/**
* 用户商品搜索
*/
@RestController
@RequestMapping("/user/goodsSearch")
public class GoodsSearchController {@DubboReferenceprivate GoodsESService goodsESService;@DubboReferenceprivate 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 {@Autowiredprivate GoodsMapper goodsMapper;@Autowiredprivate GoodsImageMapper goodsImageMapper;@DubboReferenceprivate SearchService searchService;@Overridepublic void add(Goods goods) {// 插入商品数据goodsMapper.insert(goods);// 插入图片数据Long goodsId = goods.getId(); // 获取商品主键List<GoodsImage> images = goods.getImages(); // 商品图片for (GoodsImage image : images) {image.setGoodsId(goodsId); // 给图片设置商品idgoodsImageMapper.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);}@Overridepublic void update(Goods goods) {// 删除旧图片数据Long goodsId = goods.getId(); // 商品idQueryWrapper<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); // 给图片设置商品idgoodsImageMapper.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);}@Overridepublic 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.rpmrpm -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=yesHOSTNAME=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.Driverurl: jdbc:mysql:///baizhanshopping?serverTimezone=UTCusername: rootpassword01: 123456# rabbitmqrabbitmq:host: 192.168.100.131port: 5672username: guestpassword: guestvirtual-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); // 队列名}// 交换机绑定队列@Beanpublic Binding bindQueue1(@Qualifier(GOODS_EXCHANGE) Exchange exchange,@Qualifier(SYNC_GOODS_QUEUE) Queue queue){return BindingBuilder.bind(queue).to(exchange).with("#.sync_goods.#").noargs();}@Beanpublic 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 {@Autowiredprivate GoodsMapper goodsMapper;@Autowiredprivate GoodsImageMapper goodsImageMapper;@Autowiredprivate RabbitTemplate rabbitTemplate;@Overridepublic void add(Goods goods) {// 插入商品数据goodsMapper.insert(goods);// 插入图片数据Long goodsId = goods.getId(); // 获取商品主键List<GoodsImage> images = goods.getImages(); // 商品图片for (GoodsImage image : images) {image.setGoodsId(goodsId); //给图片设置商品idgoodsImageMapper.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));}@Overridepublic void update(Goods goods) {// 删除旧图片数据Long goodsId = goods.getId(); //商品idQueryWrapper<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); //给图片设置商品idgoodsImageMapper.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));}@Overridepublic GoodsDesc findById(Long id) {return goodsMapper.findById(id);}@Overridepublic List<GoodsDesc> findAll() {return goodsMapper.findAll();}@Overridepublic void putAway(Long id, Boolean isMarketable) {goodsMapper.putAway(id,isMarketable);if (isMarketable){// 上架时同步到ESrabbitTemplate.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.Driverurl: jdbc:mysql:///baizhanshopping?serverTimezone=UTCusername: rootpassword01: 123456# elasticsearchelasticsearch:uris: http://192.168.100.131:9200# rabbitmqrabbitmq:host: 192.168.100.131port: 5672username: guestpassword: guestvirtual-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);}
}
相关文章:
亿级高并发电商项目-- 实战篇 --万达商城项目 十一(编写商品搜索功能、操作商品同步到ES、安装RabbitMQ与Erlang,配置监听队列与消息队列)
👏作者简介:大家好,我是小童,Java开发工程师,CSDN博客博主,Java领域新星创作者 📕系列专栏:前端、Java、Java中间件大全、微信小程序、微信支付、若依框架、Spring全家桶 Ǵ…...
数据结构概述和稀疏数组
数据结构和算法内容介绍 1)算法是程序的灵魂,优秀的程序可以在海量数据计算时,仍然保持高速计算 数据结构和算法概述 1)程序 数据结构算法 2)学好数据结构可以编写出更加漂亮,更加有效率的代码 3&…...
宝塔搭建实战人才求职管理系统adminm前端vue源码(三)
大家好啊,我是测评君,欢迎来到web测评。 上一期给大家分享骑士cms后台admin前端vue在本地运行打包、宝塔发布部署的方式,本期给大家分享,后台adminm移动端后台vue前端怎么在本地运行,打包,实现线上功能更新…...
服务器是干什么用的?
首先,什么是服务器?服务器是提供计算服务器和网络服务的设备。服务器和计算机由CPU、硬盘、内存、系统总线等组成。比如我们访问一个网站,点击这个网站会发出访问请求,服务器会响应服务请求,进行相应的处理,…...
C++ 之结构体与共用体
文章目录参考描述结构体使用(基本)声明初始化先创建后初始化C 11 列表初始化使用(进阶)结构数组声明(拓展)声明及创建声明及初始化匿名结构体细节外部声明与内部声明成员赋值共用体存储空间匿名共用体同化尾…...
Java基础知识汇总(良心总结)
1. 前言 本文章是对Java基础知识进行了汇总,方便大家学习。 申明:文章的内容均来自黑马程序员,博主只是将其搬到了CSDN上以共享给大家学习 2. 目录 Day01 Java学习笔记之《开章》 Day02 Java学习笔记之《运算符》 Day03 Java学习笔记之《流程…...
InnoDB之Undo log格式
1. 前言 InnoDB有两大日志模块,分别是redo log和undo log。为了避免磁盘随机写,InnoDB设计了redo log,数据写入时只写缓冲页和redo log,脏页由后台线程异步刷盘,哪怕系统崩溃也能根据redo log恢复数据。但是我们漏了一…...
一问学习StreamAPI终端操作
Java Stream管道流是用于简化集合类元素处理的java API。 在使用的过程中分为三个阶段: 将集合、数组、或行文本文件转换为java Stream管道流管道流式数据处理操作,处理管道中的每一个元素。上一个管道中的输出元素作为下一个管道的输入元素。管道流结果…...
在屎山代码中快速找到想要的代码法-锁表法(C#)
由于本人水平有限,文中有写得不对的地方请指正,本文的方法有些投机取巧,实在是没招的情况下可以酌情使用,如有侵权,请联系删除。 前几天接到一个需求,要在医嘱签署时对检验项目进行分方操作,分…...
网页设计html期末大作业
网页设计html期末大作业网页设计期末大作业-自制网站大一期末作业,外卖网站设计网页设计期末大作业-精美商城-首页框架网页设计期末大作业-自制网站 有导航栏,轮播图,按钮均可点进去,如下图所示 点我下载资源》》》》 大一期末…...
实战打靶集锦-006-Stapler
**写在前面:**记录博主的一次打靶经历。 目录1. 主机发现2. 端口发现3. 服务枚举4. 服务探查4.1 FTP探查4.1.1 匿名登录4.1.2 Elly用户4.1.3 John用户4.1.4 EXP搜索4.2 dnsmasq探查4.2.1 基础信息获取4.2.2 EXP搜索4.3 WEB应用探查4.3.1 浏览器访问4.3.2 目录扫描4.…...
致远OAA6版安装
准备工作,操作系统winserver2019,sqlserver2019。致远OA安装包0.SeeyonInstall.zip相关下载:winserver2019下载地址:cn_windows_server_2019_updated_july_2020_x64_dvd_2c9b67da.iso magnet:?xturn:btih:22A410DEA1B0886354A34D…...
python实用脚本(六)—— pandas库的使用(生成、读取表格)
本期主题: python的pandas使用 往期链接: python实用脚本(一)—— 批量修改目标文件夹下的文件名python实用脚本(二)—— 使用xlrd读取excelpython实用脚本(三)—— 通过有道智云AP…...
字符集、ASCII、GBK、UTF-8、Unicode、乱码、字符编码、解码问题等
编码解码一、背景二、字符的相关概念三、字符集3.1 ASCII[ˈski]3.1.1 ASCII的编码方式3.1.2 EASCII3.2 GBK3.2.1 GB 2312-803.2.2 GBK的制订3.2.3 GBK的实现方式3.3 Unicode(统一码、万国码)3.3.1 Unicode的出现背景3.3.2 Unicode的编写方式3.3.3 Unico…...
Java 布隆过滤器
你在么?在!一定在么?不在!一定不在么? 你想要100%的准去性,还是99%的准确性附带较高的速度和较小的资源消耗。 任何算法,任何经营收到的背后,都是时间效益 资源消耗 准确性的平衡&am…...
vscode连接服务器(腾讯云)
文章目录1. vscode远程总是报错2. vscode能连上腾讯云但密码不对或者登录后不能打开文件或文件夹1. vscode远程总是报错 报错如图所示 Could not establish connection to *** 过程试图写入的管道不存在。 在百度、csdn找了好久都是说删掉.ssh文件下的某个文件但我压根没有&a…...
IOS崩溃文件符号化实践
1.背景与项目难点 1.1 背景 由于公司之前使用的友盟要收费,filebase服务由谷歌提供,存在数据合规风险。需要实现稳定性分析功能,通过支持app崩溃信息实时采集、实时上报、实时自动解析并定位出代码问题,帮助研发同学及时定位崩溃…...
设计模式之适配器模式与桥接模式详解和应用
目录1 适配器模式1.1 定义1.2 应用场景1.3 适配器角色1.4 类适配器1.5 对象适配器1.5 接口适配器1.6 实战1.7 源码1.8 适配器与装饰器的对比1.9 适配器模式的优缺点1.10 总结2 桥接模式2.1 原理解析2.2 角色2.3 通用写法2.4 应用场景2.5 业务场景中的运用2.6 源码2.7 桥接模式优…...
Winform控件开发(14)——NotifyIcon(史上最全)
前言: 先看个气泡提示框的效果: 代码如下: 在一个button中注册click事件,当我们点击button1时,就能显示气泡 private void button1_Click(object sender, EventArgs e){notifyIcon1.Visible = true;notifyIcon1...
Verilog 学习第四节(从计数器到可控制线性序列机——LED实验进化六部曲)
从计数器到可控制线性序列机——LED实验进化六部曲一:让LED灯按照亮0.25s,灭0.75s的状态循环亮灭二:让LED灯按照亮0.25s,灭0.5s,亮0.75s,灭1s的状态循环亮灭三:让LED灯按照指定的亮灭模式亮灭&a…...
操作SSH无密登录配置
例如小编有三台服务器需要相互访问,就需要配置三台,这三台分别是hadoop102,hadoop103 , hadoop1041.打开三个服务器,分别生成hadoop102,hadoop103 , hadoop104的公钥和私钥输入命令,然后一直回车,这时候什么…...
Websocket详细介绍
需求背景 在某个资产平台,在不了解需求的情况下,我突然接到了一个任务,让我做某个页面窗口的即时通讯,想到了用websocket技术,我从来没用过,被迫接受了这个任务,我带着浓烈的兴趣,就…...
大数据书单(100本)
大数据书单(100本) 序号 书名 作者 出版社 1 Hadoop权威指南:大数据的存储与分析(第4版)(修订版)(升级版) Tom White 清华大学出版社 2 Hive编程指南 卡普廖洛 (Edward Capriolo) / 万普勒 (Dean Wampler) / 卢森格林 (Jason Rutherglen) / 曹坤 人民邮…...
python实战应用讲解-【语法基础篇】初识Python(附示例代码)
目录 前言 Python基础 基本概念: 为什么使用Python? Python2.x与3.x版本区别...
【2023保研夏令营】网安、CS(西交、华师、科、南等)
文章目录一、基本情况二、投递和入营情况三、考核情况1. 西交软院(面试)2. 川大网安(笔试面试)3. 华东师范数据学院(机试面试)4. 人大信息学院专硕(机试面试,保密)5. 南大…...
Qt COM组件导出源文件
文章目录摘要dumpcpp.exe注册COM组件COM 组件转CPP参考关键字: Qt、 COM、 组件、 源文件、 dumpcpp摘要 由于厂家提供的库不是纯净C库,是基于COM组件开的库,在和厂家友好交流无果下,只能研究下Qt 如何调用,好在Qt 的…...
各数据库数据类型的介绍和匹配
各数据库数据类型的介绍和匹配1. Oracle的数据类型2. Mysql的数据类型3. Sql server的数据类型4. 类型匹配5. Awakening1. Oracle的数据类型 数据类型介绍 VARCHAR2 :可变长度的字符串 最大长度4000 bytes 可做索引的最大长度749; NCHAR :根据字符集而定的固定长度字…...
Rancher 部署 MySQL
文章目录创建 pvc部署 MySQL前置条件:安装 rancher,可参考文章 docker 部署 rancher 创建 pvc MySQL 数据库是需要存储的,所以必须先准备 pvc 创建 pvc 自定义 pvc 名称选择已经新建好的 storageclass,storageclass 的创建可参考…...
Python语言零基础入门教程(二十五)
Python OS 文件/目录方法 Python语言零基础入门教程(二十四) 39、Python os.openpty() 方法 概述 os.openpty() 方法用于打开一个新的伪终端对。返回 pty 和 tty的文件描述符。 语法 openpty()方法语法格式如下: os.openpty()参数 无 返…...
蓝桥杯算法训练合集十五 1.打翻的闹钟2.智斗锅鸡3.文件列表
目录 1.打翻的闹钟 2.智斗锅鸡 3.文件列表 1.打翻的闹钟 问题描述 冯迭伊曼晚上刷吉米多维奇刷的太勤奋了,几乎天天迟到。崔神为了让VonDieEman改掉迟到的坏毛病,给他买了个闹钟。 一天早上,老冯被闹钟吵醒,他随手将闹钟按掉丢…...
wordpress部署https/廊坊首页霸屏优化
集群配置:1个nsqlookupd, 1个nsqadmin,3个nsqd 分区:1个order-topic,分区数为100,副本数为3 扩容时,新增一个nsqd-4。刚开始,nsqd-4没有任何分区副本。 接下来通过nsqadmin页面发现ÿ…...
哪个购物网站最便宜/设计一个公司网站多少钱
周虎摘 要:JWT(JsonWebToken)认证作为一种服务器端无状态验证方式,在分布式开发中得到了广泛的应用。但是,由于token信息的本身特点,支持的有效时间是固定的,当在token有效时间内用户操作没有完成,操作就会…...
团购网站怎么做推广/百度收录官网
报错:叫我们删除log4j-over-slf4j.jar, 选中项目 然后搜索对应的包,然后排除即可,我这边已经排除了,就ok了 ctrlF搜索哦! 版本问题,使用插件解决吧: https://yidajava.blog.csdn.net/article/details/103668676...
网站开发公司属于什么行业/在线资源搜索引擎
版权声明:欢迎转载,请注明沉默王二原创。 https://blog.csdn.net/qing_gee/article/details/43447681 init 6 重启Linux。free -m查看内存大小,已M为单位。df -h查看硬盘情况,包括大小和分区。cat /proc/cpuinfo查看CPU信息。file…...
天津网站建设费用/萝卜建站
一、情景描述: 后台给一个txt文件,编码是utf-8,在Mac电脑Xcode开发环境下读取txt文件内容,汉字会出现乱码,英文没有乱码这种情况。 二、尝试解决方法: 修改编码格式,尝试了NSUTF16StringEncoding,NSUTF8Str…...
毕设做购物网站容易吗/seo一键优化
泛型:为了让集合能够记住集合内元素各个类型,且能够达到只要编译时不出现问题,运行时就不会出现类型异常的解决方案。 泛型又从称为参数化类型,是一种编译时类型安全检测机制,类型参数的魅力在于使得程序具有可读性和…...