1.在ProductConstant中添加ProductStatusEnum
package com.atguigu.common.constant;
/**
* 商品属性类型枚举
* @author zfh
*/
public class ProductConstant {
public enum AttrEnum{
ATTR_TYPE_BASE(1,"基本属性"),ATTR_TYPE_SALE(0,"销售属性");
private int code;
private String msg;
AttrEnum(int code,String msg){
this.code = code;
this.msg = msg;
}
public int getCode() {
return code;
}
public String getMsg() {
return msg;
}
}
public enum ProductStatusEnum {
NEW_SPU(0,"新建"),
SPU_UP(1,"商品上架"),
SPU_DOWN(2,"商品下架"),
;
private int code;
private String msg;
public int getCode() {
return code;
}
public String getMsg() {
return msg;
}
ProductStatusEnum(int code, String msg) {
this.code = code;
this.msg = msg;
}
}
}
2.修改BizCodeEnume
package com.atguigu.common.exception;
/***
* 错误码和错误信息定义类
* 1. 错误码定义规则为5为数字
* 2. 前两位表示业务场景,最后三位表示错误码。例如:100001。10:通用 001:系统未知异常
* 3. 维护错误码后需要维护错误描述,将他们定义为枚举形式
* 错误码列表:
* 10: 通用
* 001:参数格式校验
* 11: 商品
* 12: 订单
* 13: 购物车
* 14: 物流
*/
public enum BizCodeEnume {
UNKNOW_EXCEPTION(10000,"系统未知异常"),
VAILD_EXCEPTION(10001,"参数格式校验失败"),
TO_MANY_REQUEST(10002,"请求流量过大,请稍后再试"),
SMS_CODE_EXCEPTION(10002,"验证码获取频率太高,请稍后再试"),
PRODUCT_UP_EXCEPTION(11000,"商品上架异常"),
USER_EXIST_EXCEPTION(15001,"存在相同的用户"),
PHONE_EXIST_EXCEPTION(15002,"存在相同的手机号"),
NO_STOCK_EXCEPTION(21000,"商品库存不足"),
LOGINACCT_PASSWORD_EXCEPTION(15003,"账号或密码错误")
;
private int code;
private String msg;
BizCodeEnume(int code,String msg){
this.code = code;
this.msg = msg;
}
public int getCode() {
return code;
}
public String getMsg() {
return msg;
}
}
3.EsConstant
package com.atguigu.gulimall.search.constant;
/**
* ES常量
* @author zfh
* @email hst1406959716@163.com
* @date 2021-12-15 09:40:46
*/
public class EsConstant {
//在es中的索引
public static final String PRODUCT_INDEX = "gulimall_product";
public static final Integer PRODUCT_PAGESIZE = 16;
}
4.在gulimall-search项目中新增ESSaveController
package com.atguigu.gulimall.search.controller;
import com.atguigu.common.exception.BizCodeEnume;
import com.atguigu.common.to.es.SkuEsModel;
import com.atguigu.common.utils.R;
import com.atguigu.gulimall.search.service.ProductSaveService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.io.IOException;
import java.util.List;
/**
* ES保存
* @author zfh
* @email hst1406959716@163.com
* @date 2021-12-15 09:40:46
*/
@RestController
@RequestMapping("/search/save")
public class ESSaveController {
@Autowired
private ProductSaveService productSaveService;
/**
* 上架商品
* @param skuEsModels
* @return
*/
@PostMapping(value = "/product")
public R productStatusUp(@RequestBody List<SkuEsModel> skuEsModels) {
boolean status = false;
try {
status = productSaveService.productStatusUp(skuEsModels);
} catch (IOException e) {
//log.error("商品上架错误{}",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();
}
}
}
5.ProductSaveService
package com.atguigu.gulimall.search.service;
import com.atguigu.common.to.es.SkuEsModel;
import java.io.IOException;
import java.util.List;
public interface ProductSaveService {
boolean productStatusUp(List<SkuEsModel> skuEsModels) throws IOException;
}
6.ProductSaveServiceImpl
package com.atguigu.gulimall.search.service.impl;
import com.alibaba.fastjson.JSON;
import com.atguigu.common.to.es.SkuEsModel;
import com.atguigu.gulimall.search.config.GulimallESConfiguration;
import com.atguigu.gulimall.search.constant.EsConstant;
import com.atguigu.gulimall.search.service.ProductSaveService;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.xcontent.XContentType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
@Slf4j
@Service("productSaveService")
public class ProductSaveServiceImpl implements ProductSaveService {
@Autowired
private RestHighLevelClient esRestClient;
@Override
public boolean productStatusUp(List<SkuEsModel> skuEsModels) throws IOException {
//1.在es中建立索引,建立号映射关系(doc/json/product-mapping.json)
//2. 在ES中保存这些数据
BulkRequest bulkRequest = new BulkRequest();
for (SkuEsModel skuEsModel : skuEsModels) {
//构造保存请求
IndexRequest indexRequest = new IndexRequest(EsConstant.PRODUCT_INDEX);
indexRequest.id(skuEsModel.getSkuId().toString());
String jsonString = JSON.toJSONString(skuEsModel);
indexRequest.source(jsonString, XContentType.JSON);
bulkRequest.add(indexRequest);
}
BulkResponse bulk = esRestClient.bulk(bulkRequest, GulimallESConfiguration.COMMON_OPTIONS);
//TODO 如果批量错误
boolean hasFailures = bulk.hasFailures();
List<String> collect = Arrays.asList(bulk.getItems()).stream().map(item -> {
return item.getId();
}).collect(Collectors.toList());
log.info("商品上架完成:{}",collect);
return hasFailures;
}
}
7.ES远程调用
在gulimall-product项目feign包下新建SearchFeignService
package com.atguigu.gulimall.product.feign;
import com.atguigu.common.to.es.SkuEsModel;
import com.atguigu.common.utils.R;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import java.util.List;
/**
* ES远程调用
* @author zfh
* @email hst1406959716@163.com
* @date 2021-12-15 09:50:46
*/
@FeignClient("gulimall-search")
public interface SearchFeignService {
@PostMapping(value = "/search/save/product")
R productStatusUp(@RequestBody List<SkuEsModel> skuEsModels);
}
8.SpuInfoDao中新增updaSpuStatus
package com.atguigu.gulimall.product.dao;
import com.atguigu.gulimall.product.entity.SpuInfoEntity;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
/**
* spu信息
*
* @author zfh
* @email hst1406959716@163.com
* @date 2021-10-14 09:40:46
*/
@Mapper
public interface SpuInfoDao extends BaseMapper<SpuInfoEntity> {
void updaSpuStatus(@Param("spuId") Long spuId, @Param("code") int code);
}
9.SpuInfoDao.xml
<update id="updaSpuStatus">
UPDATE pms_spu_info SET publish_status = #{code} ,update_time = NOW() WHERE id = #{spuId}
</update>
10.SpuInfoServiceImpl中up方法实现
/**
* 商品上架
* @param spuId
*/
@Override
public void up(Long spuId) {
//1.查询当前spuId对应的sku
List<SkuInfoEntity> skuInfoEntityList = skuInfoService.getSkusBySpuId(spuId);
List<Long> spuIdList = skuInfoEntityList.stream().map(SkuInfoEntity::getSkuId).collect(Collectors.toList());
//4.查询当前sku所有可被检索的规格属性
List<ProductAttrValueEntity> productAttrValueEntityList = productAttrValueService.baseAttrListForSPU(spuId);
List<Long> attrIds = productAttrValueEntityList.stream().map(productAttrValue -> {
return productAttrValue.getAttrId();
}).collect(Collectors.toList());
//查找可被检索的attrId
List<Long> searchAttrIds = attrService.selectSearchAttrIds(attrIds);
HashSet<Long> attrIdSet = new HashSet<>(searchAttrIds);
// Set<Long> attrIdSet = searchAttrIds.stream().collect(Collectors.toSet());
List<SkuEsModel.Attrs> attrsList = productAttrValueEntityList.stream().filter(item ->
attrIdSet.contains(item.getAttrId())
).map(item -> {
SkuEsModel.Attrs attrs = new SkuEsModel.Attrs();
BeanUtils.copyProperties(item, attrs);
return attrs;
}).collect(Collectors.toList());
Map<Long, Boolean> stockMap = null;
try{
//1.发送远程调用,库存系统查询是否有库存
R skuHasStock = wareFeignService.getSkuHasStock(spuIdList);
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.getMessage());
}
//封装每个sku的信息
Map<Long, Boolean> finalStockMap = stockMap;
List<SkuEsModel> skuEsModelList = skuInfoEntityList.stream().map(skuInfoEntity -> {
SkuEsModel skuEsModel = new SkuEsModel();
BeanUtils.copyProperties(skuInfoEntity,skuEsModel);
//设置skuPrice,skuImg
skuEsModel.setSkuPrice(skuInfoEntity.getPrice());
skuEsModel.setSkuImg(skuInfoEntity.getSkuDefaultImg());
//设置库存
if(finalStockMap == null){
skuEsModel.setHasStock(true);
}else{
skuEsModel.setHasStock(finalStockMap.get(skuInfoEntity.getSkuId()));
}
//2.热度评分
skuEsModel.setHotScore(0L); //暂时设为0 TODO
//3.查询品牌和分类的名字
BrandEntity brandEntity = brandService.getById(skuEsModel.getBrandId());
skuEsModel.setBrandName(brandEntity.getName());
skuEsModel.setBrandImg(brandEntity.getLogo());
CategoryEntity categoryEntity = categoryService.getById(skuEsModel.getCatalogId());
skuEsModel.setCatalogName(categoryEntity.getName());
//设置检索属性
skuEsModel.setAttrs(attrsList);
return skuEsModel;
}).collect(Collectors.toList());
//5.将数据发送给es进行保存
R r = searchFeignService.productStatusUp(skuEsModelList);
if (r.getCode() == 0) {
//远程调用成功
//6、修改当前spu的状态
this.baseMapper.updaSpuStatus(spuId, ProductConstant.ProductStatusEnum.SPU_UP.getCode());
} else {
//远程调用失败
//7、重复调用?接口幂等性:重试机制
/**
* feign调用流程
*
* 1.构造请求数据,将对象转json
* RequestTemplate template = this.buildTemplateFromArgs.create(argv);
* 2.发送请求进行执行(执行成功会解码响应数据)
* this.executeAndDecode(template, options);
* 3.
* retryer.continueOrPropagate(e);
*/
}
}