只有上架的商品才可以进行检索
@PostMapping("/{spuId}/up")
public R spuUp(@PathVariable("spuId") Long spuId){
spuInfoService.up(spuId);
return R.ok();
}
创建一个to对象,与我们之前配置的索引相匹配
@Data
public class SkuEsModel { //common中
private Long skuId;
private Long spuId;
private String skuTitle;
private BigDecimal skuPrice;
private String skuImg;
private Long saleCount;
private boolean hasStock;
private Long hotScore;
private Long brandId;
private Long catalogId;
private String brandName;
private String brandImg;
private String catalogName;
private List<Attr> attrs;
@Data
public static class Attr {
private Long attrId;
private String attrName;
private String attrValue;
}
}
查询List<attr>分类的时候用到的方法selectSearchAttrIds
经过了service接口-service实现类-baseDao接口-daoMapping
<select id="selectSearchAttrIds" resultType="java.lang.Long">
select attr_id from pms_attr where attr_id in
<foreach collection="attrIds" item="id" separator="," open="(" close=")">
#{id}
</foreach>
and search_type = 1
</select>
编写远程接口
ware下的vo用于返回数据,product下面也放一份()
@Data
public class SkuHasStockVo {
private Long skuId;
private Boolean hasStock;
}
@PostMapping("/hasstock")
public R<List<SkuHasStockVo>> getSkuHasStock(@RequestBody List<Long> skuIds){
List<SkuHasStockVo> skuHasStockVos=wareSkuService.getSkuHasStock(skuIds);
R<List<SkuHasStockVo>> ok = R.ok();
ok.setData(skuHasStockVos);
return ok;
}
传过来的id有可能会有数据库里没有的值,于是会查出来null值,所以要排空
@Override
public List<SkuHasStockVo> getSkuHasStock(List<Long> skuIds) {
//获取sku总库存数stock-stock_locked
List<WareSkuEntity> wareSkuEntityList = this.list(new QueryWrapper<WareSkuEntity>().in("sku_id", skuIds));
List<SkuHasStockVo> collect = wareSkuEntityList.stream().filter(w->{
return !(w==null);//排除为空的值
}).map(w -> {
SkuHasStockVo skuHasStockVo = new SkuHasStockVo();
skuHasStockVo.setSkuId(w.getSkuId());
if(w.getStock()==null||w.getStockLocked()==null){
skuHasStockVo.setHasStock(true);
}else{
skuHasStockVo.setHasStock((w.getStock() - w.getStockLocked()) > 0);
}
return skuHasStockVo;
}).collect(Collectors.toList());
return collect;
}
查询库存服务的远程接口:
@FeignClient("gulimall-ware")
public interface WareFeignService {
@PostMapping("/ware/waresku/hasstock")
R<List<SkuHasStockVo>> getSkuHasStock(@RequestBody List<Long> skuIds);
}
增加工具类R的泛型方法
public class R<T> extends HashMap<String, Object> {
private static final long serialVersionUID = 1L;
private T data;
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
.....
}
ES模块接受数据,创建索引
public class EsConstant {
public static final String PRODUCT_INDEX="product";//sku数据在product中的索引
}
@RequestMapping("/search/save")
@RestController
@Slf4j
public class ESSaveController {
@Autowired
ProductSaveService productSaveService;
//上架商品
@PostMapping("/product")
public R productStatusUp(@RequestBody List<SkuEsModel> skuEsModels){
boolean b=false;
try{
b = productSaveService.productStatusUp(skuEsModels);
}catch (Exception e){
log.error("商品上架错误:{}",e);
return R.error(BizCodeEnum.PRODUCT_UP_EXCEPTION.getCode(),BizCodeEnum.PRODUCT_UP_EXCEPTION.getMsg());
}
if(b){
return R.ok();
}else {
return R.error(BizCodeEnum.PRODUCT_UP_EXCEPTION.getCode(),BizCodeEnum.PRODUCT_UP_EXCEPTION.getMsg());
}
}
}
@Slf4j
@Service
public class ProductSaveServiceImpl implements ProductSaveService {
@Autowired
RestHighLevelClient restHighLevelClient;
@Override
public boolean productStatusUp(List<SkuEsModel> skuEsModels) throws IOException {
//保存到es
//1.建立一个索引,product映射关系
//2.给es中保存这些数据
BulkRequest bulkRequest=new BulkRequest();//批量保存数据
for (SkuEsModel skuEsModel : skuEsModels) {
//1.构造保存请求
IndexRequest indexRequest=new IndexRequest(EsConstant.PRODUCT_INDEX);
indexRequest.id(skuEsModel.getSkuId().toString());
String s = JSON.toJSONString(skuEsModel);
indexRequest.source(s, XContentType.JSON);
bulkRequest.add(indexRequest);
}
BulkResponse bulk = restHighLevelClient.bulk(bulkRequest, ESConfig.COMMON_OPTIONS);
List<String> collect = Arrays.stream(bulk.getItems()).map(i -> {
return i.getId();
}).collect(Collectors.toList());
log.info("商品上架成功",collect);
boolean b = bulk.hasFailures();
return !b;
}
}
远程接口声明
@FeignClient("gulimall-search")
public interface SearchFeignService {
@PostMapping("/search/save/product")
R productStatusUp(@RequestBody List<SkuEsModel> skuEsModels);
}
更新商品状态的函数
<update id="updateSpuStatus">
update pms_spu_info set publish_status=#{code},update_time=now() where id=#{spuId}
</update>
上架的方法
@Override
public void up(Long spuId) {
//对于skuInfoEntity和ESModel对象
//属性有但是字段不同:skuPrice skuImg
//没有属性需要单独处理:hasStock(需要远程调用库存系统,返回一个bool值),hotScore(热度评分,默认放一个0)
//需要调用本地方法查询:brandName,brandImg,catalogName,List<attrs>(attrId,attrName,attrValue)
List<SkuInfoEntity> skuInfoEntityList = skuInfoService.list(new QueryWrapper<SkuInfoEntity>().eq("spu_id", spuId));
//避免循环查表,先查询brandName和brandImg根据brandId
SpuInfoEntity byId = this.getById(spuId);
Long brandId = byId.getBrandId();
BrandEntity brandEntity = brandService.getById(brandId);
String brandEntityName = brandEntity.getName();
String logo = brandEntity.getLogo();
//查找catlogName
Long catalogId = byId.getCatalogId();
CategoryEntity categoryEntity = categoryService.getById(catalogId);
String categoryEntityName = categoryEntity.getName();
//查找List<attr>
List<ProductAttrValueEntity> list = productAttrValueService.baseAttrListForSpu(spuId);//获得spu相关的所有pav属性
List<Long> attrIds = list.stream().map(item -> {
return item.getAttrId();
}).collect(Collectors.toList());
//在指定的索引集合里挑出检索类型search_type为1的值
List<Long> longList=attrService.selectSearchAttrIds(attrIds);
Set<Long> idSet=new HashSet<>(longList);//存储进一个临时集合
List<SkuEsModel.Attr> attrList = list.stream().filter(item -> {
return idSet.contains(item.getAttrId());//如果在我们收集到的集合里有就返回true
}).map(item -> {
SkuEsModel.Attr attr = new SkuEsModel.Attr();
BeanUtils.copyProperties(item, attr);
return attr;
}).collect(Collectors.toList());
//编写库存查询--远程查询
List<Long> skuIds = skuInfoEntityList.stream().map(w -> {
return w.getSkuId();
}).collect(Collectors.toList());
Map<Long, Boolean> map=null;
try{
R<List<SkuHasStockVo>> skuHasStock = wareFeignService.getSkuHasStock(skuIds);
List<SkuHasStockVo> data = skuHasStock.getData();
//根据skuid和bool值组合成了一个map
map= data.stream().collect(Collectors.toMap(SkuHasStockVo::getSkuId, item -> item.getHasStock()));
}catch (Exception e){
log.error("库存服务查询异常:原因{}",e);
}
Map<Long, Boolean> finalMap = map;//map要使用只能经过一次赋值
List<SkuEsModel> collect = skuInfoEntityList.stream().map(item -> {
SkuEsModel skuEsModel = new SkuEsModel();
BeanUtils.copyProperties(item,skuEsModel);//复制属性
skuEsModel.setSkuImg(item.getSkuDefaultImg());
skuEsModel.setSkuPrice(item.getPrice());
skuEsModel.setBrandName(brandEntityName);
skuEsModel.setBrandImg(logo);
skuEsModel.setCatalogName(categoryEntityName);
skuEsModel.setAttrs(attrList);
skuEsModel.setHotScore(0L);
if (finalMap == null) {
skuEsModel.setHasStock(true);
} else {
skuEsModel.setHasStock(finalMap.get(item.getSkuId()));
}
return skuEsModel;
}).collect(Collectors.toList());
//数据发给es进行保存
R r = searchFeignService.productStatusUp(collect);
if(r.getCode()==0){
//修改当前spu的状态
this.baseMapper.updateSpuStatus(spuId, ProductConstant.StatusEnum.STU_UP.getCode());
}else{
//远程调用失败
//TODo 重复调用,接口幂等性,重试机制
}
}
}
细化与bug
在调试的时候发现,当我们去拿到库存服务里的data的时候,data是没有值的
这是因为我们的R是一个哈希map,jackson对于Hashmap类型会有特殊的处理方式,对类会直接向上转型为map导致私有属性的消失
调整R
public class R extends HashMap<String, Object> {
private static final long serialVersionUID = 1L;
private R setData(Object o){
put("data",o);
return this;
}
//利用fastJson进行逆转
public<T> T getData(TypeReference<T> typeReference){
Object data=get("data");
String s = JSON.toJSONString(data);
T t=JSON.parseObject(s,typeReference);
return t;
}
@PostMapping("/hasstock")
public R getSkuHasStock(@RequestBody List<Long> skuIds){
List<SkuHasStockVo> skuHasStockVos=wareSkuService.getSkuHasStock(skuIds);
return R.ok().setData(skuHasStockVos);
}
@Transactional
@Override
public void up(Long spuId) {
//对于skuInfoEntity和ESModel对象
//属性有但是字段不同:skuPrice skuImg
//没有属性需要单独处理:hasStock(需要远程调用库存系统,返回一个bool值),hotScore(热度评分,默认放一个0)
//需要调用本地方法查询:brandName,brandImg,catalogName,List<attrs>(attrId,attrName,attrValue)
List<SkuInfoEntity> skuInfoEntityList = skuInfoService.list(new QueryWrapper<SkuInfoEntity>().eq("spu_id", spuId));
//避免循环查表,先查询brandName和brandImg根据brandId
SpuInfoEntity byId = this.getById(spuId);
Long brandId = byId.getBrandId();
BrandEntity brandEntity = brandService.getById(brandId);
String brandEntityName = brandEntity.getName();
String logo = brandEntity.getLogo();
//查找catlogName
Long catalogId = byId.getCatalogId();
CategoryEntity categoryEntity = categoryService.getById(catalogId);
String categoryEntityName = categoryEntity.getName();
//查找List<attr>
List<ProductAttrValueEntity> list = productAttrValueService.baseAttrListForSpu(spuId);//获得spu相关的所有pav属性
List<Long> attrIds = list.stream().map(item -> {
return item.getAttrId();
}).collect(Collectors.toList());
//在指定的索引集合里挑出检索类型search_type为1的值
List<Long> longList=attrService.selectSearchAttrIds(attrIds);
Set<Long> idSet=new HashSet<>(longList);//存储进一个临时集合
List<SkuEsModel.Attr> attrList = list.stream().filter(item -> {
return idSet.contains(item.getAttrId());//如果在我们收集到的集合里有就返回true
}).map(item -> {
SkuEsModel.Attr attr = new SkuEsModel.Attr();
BeanUtils.copyProperties(item, attr);
return attr;
}).collect(Collectors.toList());
//编写库存查询--远程查询
List<Long> skuIds = skuInfoEntityList.stream().map(w -> {
return w.getSkuId();
}).collect(Collectors.toList());
Map<Long, Boolean> map=null;
try{
R r=wareFeignService.getSkuHasStock(skuIds);
TypeReference<List<SkuHasStockVo>> typeReference = new TypeReference<List<SkuHasStockVo>>() {};//构造器受保护我们拿不到,只能携程一个匿名类对象
//根据skuid和bool值组合成了一个map
List<SkuHasStockVo> data = r.getData(typeReference);
data.stream().collect(Collectors.toMap(SkuHasStockVo::getSkuId, item -> item.getHasStock()));
}catch (Exception e){
log.error("库存服务查询异常:原因{}",e);
}
Map<Long, Boolean> finalMap = map;//map要使用只能经过一次赋值
List<SkuEsModel> collect = skuInfoEntityList.stream().map(item -> {
SkuEsModel skuEsModel = new SkuEsModel();
BeanUtils.copyProperties(item,skuEsModel);//复制属性
skuEsModel.setSkuImg(item.getSkuDefaultImg());
skuEsModel.setSkuPrice(item.getPrice());
skuEsModel.setBrandName(brandEntityName);
skuEsModel.setBrandImg(logo);
skuEsModel.setCatalogName(categoryEntityName);
skuEsModel.setAttrs(attrList);
skuEsModel.setHotScore(0L);
if (finalMap == null) {
skuEsModel.setHasStock(true);
} else {
skuEsModel.setHasStock(finalMap.get(item.getSkuId()));
}
return skuEsModel;
}).collect(Collectors.toList());
//数据发给es进行保存
R r = searchFeignService.productStatusUp(collect);
if(r.getCode()==0){
//修改当前spu的状态
this.baseMapper.updateSpuStatus(spuId, ProductConstant.StatusEnum.STU_UP.getCode());
}else{
//远程调用失败
//TODo 重复调用,接口幂等性,重试机制
}
}
再次debug已经有值了