15.商品上架
只有上架的商品才可以进行检索
@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
一定不要用collection=#{}取出来,会取出一个空值,这是个天坑
<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>
上架的方法
@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); Map<Long, Boolean> collect = data.stream().collect(Collectors.toMap(SkuHasStockVo::getSkuId, SkuHasStockVo::getHasStock)); map=collect; }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(item.getSkuId()!=null&&finalMap.containsKey(item.getSkuId())){ skuEsModel.setHasStock(finalMap==null?false: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已经有值了