1. 创建一个查询服务,pom中引入es服务所需要的包
<dependency> <groupId>org.elasticsearch.client</groupId> <artifactId>elasticsearch-rest-high-level-client</artifactId> <version>7.4.2</version> </dependency>
2. 创建配置类,在类中指定es服务连接信息
@Configuration public class GulimallElasticSearchConfig { public static final RequestOptions COMMON_OPTIONS; static { RequestOptions.Builder builder = RequestOptions.DEFAULT.toBuilder(); // builder.addHeader("Authorization", "Bearer " + TOKEN); // builder.setHttpAsyncResponseConsumerFactory( // new HttpAsyncResponseConsumerFactory // .HeapBufferedResponseConsumerFactory(30 * 1024 * 1024 * 1024)); COMMON_OPTIONS = builder.build(); } @Bean public RestHighLevelClient esRestClient(){ RestHighLevelClient client = new RestHighLevelClient( RestClient.builder(new HttpHost("123.xx.xx.xx", 9200, "http"))); return client; } }
3. 在查询服务中,对外提供保存到es的接口,在保存之前先在使用kibana在es中建立product索引的映射关系
PUT product
{
"mappings": {
"properties": {
"skuId": {
"type": "long"
},
"spuId": {
"type": "long"
},
"skuTitle": {
"type": "text",
"analyzer": "ik_smart"
},
"skuPrice": {
"type": "keyword"
},
"skuImg": {
"type": "keyword",
"index": false,
"doc_values": false
},
"saleCount": {
"type": "long"
},
"hosStock": {
"type": "boolean"
},
"hotScore": {
"type": "long"
},
"brandId": {
"type": "long"
},
"catelogId": {
"type": "long"
},
"brandName": {
"type": "keyword",
"index": false,
"doc_values": false
},
"brandImg": {
"type": "keyword",
"index": false,
"doc_values": false
},
"catelogName": {
"type": "keyword",
"index": false,
"doc_values": false
},
"attrs": {
"type": "nested",
"properties": {
"attrId": {
"type": "long"
},
"attrName": {
"type": "keyword",
"index": false,
"doc_values": false
},
"attrValue": {
"type": "keyword"
}
}
}
}
}
}
GET product/_search
在controller类中接口实现类中,实现保存es的方法
@Slf4j @RequestMapping(value = "/search/save") @RestController public class ElasticSaveController { @Autowired private ProductSaveService productSaveService; @PostMapping(value = "/product") public R productStatusUp(@RequestBody List<SkuEsModel> skuEsModels) { boolean status=false; try { status = productSaveService.productStatusUp(skuEsModels); } catch (IOException e) { return R.error(BizCodeEnume.PRODUCT_UP_EXCEPTION.getCode(),BizCodeEnume.PRODUCT_UP_EXCEPTION.getMsg()); } if(status){ return R.error(BizCodeEnume.PRODUCT_UP_EXCEPTION.getCode(),BizCodeEnume.PRODUCT_UP_EXCEPTION.getMsg()); }else { return R.ok(); } } }
@Slf4j @Service("productSaveService") public class ProductSaveServiceImpl implements ProductSaveService { @Autowired private RestHighLevelClient esRestClient; @Override public boolean productStatusUp(List<SkuEsModel> skuEsModels) throws IOException { //1. 在es中建立索引,建立映射关系 //2. 数据保存到es中 BulkRequest bulkRequest = new BulkRequest(); for (SkuEsModel model : skuEsModels){ IndexRequest indexRequest = new IndexRequest(EsConstant.PRODUCT_INDEX); indexRequest.id(model.getSkuId().toString()); String s = JSON.toJSONString(model); indexRequest.source(s, XContentType.JSON); bulkRequest.add(indexRequest); } BulkResponse bulk = esRestClient.bulk(bulkRequest, GulimallElasticSearchConfig.COMMON_OPTIONS); //如果发生错误 boolean b = bulk.hasFailures(); List<String> list = Arrays.stream(bulk.getItems()).map(item -> item.getId()).collect(Collectors.toList()); log.info("商品上架:{}",list); return b; } }
4. 在业务服务中,提供feign接口,同时在业务controller中调用对外暴露的feign接口,实现调用接口来把数据保存到es中
@FeignClient("gulimall-search") public interface SearchFeignService { @PostMapping(value = "/search/save/product") public R productStatusUp(@RequestBody List<SkuEsModel> skuEsModels); }
//商品上架 ///product/spuinfo/{spuId}/up @PostMapping(value = "/{spuId}/up") public R spuUp(@PathVariable("spuId") Long spuId) { spuInfoService.up(spuId); return R.ok(); }
@Override public void up(Long spuId) { //1、查出当前spuId对应的所有sku信息,品牌的名字 List<SkuInfoEntity> skuInfoEntities = skuInfoService.getSkusBySpuId(spuId); //TODO 4、查出当前sku的所有可以被用来检索的规格属性 List<ProductAttrValueEntity> baseAttrs = attrValueService.baseAttrlistforspu(spuId); List<Long> attrIds = baseAttrs.stream().map(attr -> { return attr.getAttrId(); }).collect(Collectors.toList()); List<Long> searchAttrIds = attrService.selectSearchAttrs(attrIds); //转换为Set集合 Set<Long> idSet = searchAttrIds.stream().collect(Collectors.toSet()); List<SkuEsModel.Attrs> attrsList = baseAttrs.stream().filter(item -> { return idSet.contains(item.getAttrId()); }).map(item -> { SkuEsModel.Attrs attrs = new SkuEsModel.Attrs(); BeanUtils.copyProperties(item, attrs); return attrs; }).collect(Collectors.toList()); List<Long> skuIdList = skuInfoEntities.stream() .map(SkuInfoEntity::getSkuId) .collect(Collectors.toList()); //TODO 1、发送远程调用,库存系统查询是否有库存 Map<Long, Boolean> stockMap = null; try { R skuHasStock = wareFeignService.getSkuHasStock(skuIdList); TypeReference<List<SkuHasStockVo>> typeReference = new TypeReference<List<SkuHasStockVo>>() {}; stockMap = skuHasStock.getData(typeReference).stream() .collect(Collectors.toMap(SkuHasStockVo::getSkuId, item -> item.getHasStock())); }catch (Exception e){ log.error("库存服务查询异常:原因{}",e); } //2、封装每个sku的信息 Map<Long, Boolean> finalStockMap = stockMap; List<SkuEsModel> collect = skuInfoEntities.stream().map(sku -> { //组装需要的数据 SkuEsModel esModel = new SkuEsModel(); esModel.setSkuPrice(sku.getPrice()); esModel.setSkuImg(sku.getSkuDefaultImg()); //设置库存信息 if (finalStockMap == null) { esModel.setHasStock(true); } else { esModel.setHasStock(finalStockMap.get(sku.getSkuId())); } //TODO 2、热度评分。0 esModel.setHotScore(0L); //TODO 3、查询品牌和分类的名字信息 BrandEntity brandEntity = brandService.getById(sku.getBrandId()); esModel.setBrandName(brandEntity.getName()); esModel.setBrandId(brandEntity.getBrandId()); esModel.setBrandImg(brandEntity.getLogo()); CategoryEntity categoryEntity = categoryService.getById(sku.getCatalogId()); esModel.setCatalogId(categoryEntity.getCatId()); esModel.setCatalogName(categoryEntity.getName()); //设置检索属性 esModel.setAttrs(attrsList); BeanUtils.copyProperties(sku,esModel); return esModel; }).collect(Collectors.toList()); //TODO 5、将数据发给es进行保存:gulimall-search R r = searchFeignService.productStatusUp(collect); if (r.getCode() == 0) { //远程调用成功 //TODO 6、修改当前spu的状态 this.baseMapper.updaSpuStatus(spuId, ProductConstant.ProductStatusEnum.SPU_UP.getCode()); } else { //远程调用失败 //TODO 7、重复调用?接口幂等性:重试机制 } }
5. 当调用商品服务的上架方法后,会把商品保存到es中,在kibana中查询product索引,可以看到保存的商品数据
GET product/_search
{ "took" : 127, "timed_out" : false, "_shards" : { "total" : 1, "successful" : 1, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : { "value" : 1, "relation" : "eq" }, "max_score" : 1.0, "hits" : [ { "_index" : "product", "_type" : "_doc", "_id" : "27", "_score" : 1.0, "_source" : { "attrs" : [ { "attrId" : 15, "attrName" : "CPU品牌", "attrValue" : "高通(Qualcomm)" }, { "attrId" : 16, "attrName" : "CPU型号", "attrValue" : "骁龙665" } ], "brandId" : 10, "brandImg" : "https://gulimall-hello.oss-cn-beijing.aliyuncs.com/2019-11-18/1f9e6968-cf92-462e-869a-4c2331a4113f_xiaomi.png", "brandName" : "小米", "catalogId" : 225, "catalogName" : "手机", "hasStock" : true, "hotScore" : 0, "saleCount" : 0, "skuId" : 27, "skuImg" : "https://lewang-yygh.oss-cn-hangzhou.aliyuncs.com/2023-07-23//5a07c9a9-d223-4ae5-873a-38b259ec7267_6a1b2703a9ed8737.jpg", "skuPrice" : 0.0, "skuTitle" : "xiaomi11 白色 6GB", "spuId" : 15 } } ] } }