15.商品上架

该博客主要介绍了如何在商品上架时将商品信息同步至 Elasticsearch 搜索引擎,包括创建 SKU ES Model 对象,处理库存服务的远程调用,解决因 JSON 序列化导致的数据丢失问题,以及在服务间通过 Feign 进行远程调用。同时,展示了如何处理商品状态更新,确保商品状态变更后及时更新到搜索引擎中。
摘要由CSDN通过智能技术生成

只有上架的商品才可以进行检索

    @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已经有值了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值